├── certinfo.ico ├── screenshot.png ├── src ├── certinfo.png ├── qml.qrc ├── qt5.qrc ├── qt6.qrc ├── thirdparty │ └── SortFilterProxyModel │ │ ├── qvariantlessthan.h │ │ ├── filters │ │ ├── alloffilter.h │ │ ├── anyoffilter.h │ │ ├── rolefilter.h │ │ ├── valuefilter.h │ │ ├── filtercontainerfilter.cpp │ │ ├── filtercontainerfilter.h │ │ ├── indexfilter.h │ │ ├── alloffilter.cpp │ │ ├── expressionfilter.h │ │ ├── regexpfilter.h │ │ ├── filter.h │ │ ├── filtersqmltypes.cpp │ │ ├── rolefilter.cpp │ │ ├── rangefilter.h │ │ ├── anyoffilter.cpp │ │ ├── valuefilter.cpp │ │ ├── filtercontainer.h │ │ ├── filter.cpp │ │ ├── regexpfilter.cpp │ │ ├── indexfilter.cpp │ │ ├── filtercontainer.cpp │ │ ├── rangefilter.cpp │ │ └── expressionfilter.cpp │ │ ├── utils │ │ ├── utils.h │ │ └── utils.cpp │ │ ├── proxyroles │ │ ├── filterrole.h │ │ ├── singlerole.h │ │ ├── proxyrolesqmltypes.cpp │ │ ├── joinrole.h │ │ ├── proxyrole.h │ │ ├── expressionrole.h │ │ ├── regexprole.h │ │ ├── proxyrole.cpp │ │ ├── singlerole.cpp │ │ ├── proxyrolecontainer.h │ │ ├── filterrole.cpp │ │ ├── switchrole.h │ │ ├── proxyrolecontainer.cpp │ │ ├── joinrole.cpp │ │ ├── regexprole.cpp │ │ ├── expressionrole.cpp │ │ └── switchrole.cpp │ │ ├── sorters │ │ ├── rolesorter.h │ │ ├── sortersqmltypes.cpp │ │ ├── filtersorter.h │ │ ├── expressionsorter.h │ │ ├── stringsorter.h │ │ ├── sorter.h │ │ ├── sortercontainer.h │ │ ├── rolesorter.cpp │ │ ├── filtersorter.cpp │ │ ├── stringsorter.cpp │ │ ├── sorter.cpp │ │ ├── sortercontainer.cpp │ │ └── expressionsorter.cpp │ │ ├── LICENSE │ │ ├── SortFilterProxyModel.pri │ │ ├── qvariantlessthan.cpp │ │ ├── README.md │ │ └── qqmlsortfilterproxymodel.h ├── +qt5 │ ├── ExportFileDialog.qml │ └── ImportHostsFileDialog.qml ├── +qt6 │ ├── ExportFileDialog.qml │ └── ImportHostsFileDialog.qml ├── listmodel │ ├── domaincountlistmodel.h │ ├── domaincountlistmodel.cpp │ ├── caissuerlistmodel.h │ ├── qabstractlistmodelwithrowcountsignal.h │ ├── caissuerlistmodel.cpp │ └── genericlistmodel.h ├── ca │ ├── caprocessor.h │ ├── caconcurrentgatherer.h │ ├── certificate.h │ └── caprocessor.cpp ├── versioncheck │ ├── versioncheck.h │ └── versioncheck.cpp ├── domainsources │ ├── domainslisttextfile.h │ ├── browserhistorydb.h │ ├── domainslisttextfile.cpp │ └── browserhistorydb.cpp ├── FoldableCertInfo.qml └── main.cpp ├── README.md ├── flake.nix ├── nix └── derivation.nix ├── .github └── workflows │ └── qmake.yml ├── .gitignore ├── flake.lock ├── CertInfo.pro └── setup.iss /certinfo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaymiiOrg/CertInfo/master/certinfo.ico -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaymiiOrg/CertInfo/master/screenshot.png -------------------------------------------------------------------------------- /src/certinfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaymiiOrg/CertInfo/master/src/certinfo.png -------------------------------------------------------------------------------- /src/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | FoldableCertInfo.qml 5 | certinfo.png 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/qt5.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | +qt5/ExportFileDialog.qml 4 | +qt5/ImportHostsFileDialog.qml 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/qt6.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | +qt6/ExportFileDialog.qml 4 | +qt6/ImportHostsFileDialog.qml 5 | 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CertInfo 2 | 3 | Which Root Certificate should you trust? Find out with CertInfo 4 | 5 | Please see [my site for more information](https://raymii.org/s/software/Which_Root_Certificates_Should_You_Trust_CertInfo.html) 6 | 7 | 8 | 9 | ![screenshot](screenshot.png) 10 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/qvariantlessthan.h: -------------------------------------------------------------------------------- 1 | #ifndef QVARIANTLESSTHAN_H 2 | #define QVARIANTLESSTHAN_H 3 | 4 | #include 5 | 6 | namespace qqsfpm { 7 | 8 | bool lessThan(const QVariant &lhs, const QVariant &rhs); 9 | 10 | } // namespace qqsfpm 11 | 12 | #endif // QVARIANTLESSTHAN_H 13 | -------------------------------------------------------------------------------- /src/+qt5/ExportFileDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Dialogs 1.3 3 | 4 | FileDialog { 5 | required property var proc 6 | id: root 7 | title: "Export results to which file" 8 | selectExisting: false 9 | nameFilters: [ "text file (*.txt)", "All files (*)" ] 10 | onAccepted: { 11 | proc.exportToText(root.fileUrl) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/+qt6/ExportFileDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Dialogs 3 | 4 | FileDialog { 5 | required property var proc 6 | id: root 7 | title: "Export results to which file" 8 | fileMode: FileDialog.SaveFile 9 | nameFilters: [ "text file (*.txt)", "All files (*)" ] 10 | onAccepted: { 11 | proc.exportToText(root.selectedFile) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/alloffilter.h: -------------------------------------------------------------------------------- 1 | #ifndef ALLOFFILTER_H 2 | #define ALLOFFILTER_H 3 | 4 | #include "filtercontainerfilter.h" 5 | 6 | namespace qqsfpm { 7 | 8 | class AllOfFilter : public FilterContainerFilter { 9 | Q_OBJECT 10 | 11 | public: 12 | using FilterContainerFilter::FilterContainerFilter; 13 | 14 | protected: 15 | bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 16 | }; 17 | 18 | } 19 | 20 | #endif // ALLOFFILTER_H 21 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/anyoffilter.h: -------------------------------------------------------------------------------- 1 | #ifndef ANYOFFILTER_H 2 | #define ANYOFFILTER_H 3 | 4 | #include "filtercontainerfilter.h" 5 | 6 | namespace qqsfpm { 7 | 8 | class AnyOfFilter : public FilterContainerFilter { 9 | Q_OBJECT 10 | 11 | public: 12 | using FilterContainerFilter::FilterContainerFilter; 13 | 14 | protected: 15 | bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 16 | }; 17 | 18 | } 19 | 20 | #endif // ANYOFFILTER_H 21 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "A very basic flake"; 3 | 4 | inputs = { 5 | nixpkgs.url = github:NixOS/nixpkgs/nixpkgs-unstable; 6 | flake-utils.url = "github:numtide/flake-utils"; 7 | }; 8 | 9 | outputs = { 10 | self, 11 | nixpkgs, 12 | flake-utils, 13 | }: 14 | flake-utils.lib.eachDefaultSystem (system: let 15 | pkgs = nixpkgs.legacyPackages.${system}; 16 | in { 17 | defaultPackage = 18 | pkgs.qt6.callPackage ./nix/derivation.nix { 19 | }; 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/utils/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include 5 | 6 | namespace qqsfpm { 7 | 8 | int compareVariants(const QVariant &lhs, const QVariant &rhs); 9 | 10 | inline bool operator<(const QVariant &lhs, const QVariant &rhs) { return compareVariants(lhs, rhs) < 0; } 11 | inline bool operator<=(const QVariant &lhs, const QVariant &rhs) { return compareVariants(lhs, rhs) <= 0; } 12 | inline bool operator>(const QVariant &lhs, const QVariant &rhs) { return compareVariants(lhs, rhs) > 0; } 13 | inline bool operator>=(const QVariant &lhs, const QVariant &rhs) { return compareVariants(lhs, rhs) >= 0; } 14 | 15 | } 16 | 17 | #endif // UTILS_H 18 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/rolefilter.h: -------------------------------------------------------------------------------- 1 | #ifndef ROLEFILTER_H 2 | #define ROLEFILTER_H 3 | 4 | #include "filter.h" 5 | 6 | namespace qqsfpm { 7 | 8 | class RoleFilter : public Filter 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(QString roleName READ roleName WRITE setRoleName NOTIFY roleNameChanged) 12 | 13 | public: 14 | using Filter::Filter; 15 | 16 | const QString& roleName() const; 17 | void setRoleName(const QString& roleName); 18 | 19 | Q_SIGNALS: 20 | void roleNameChanged(); 21 | 22 | protected: 23 | QVariant sourceData(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const; 24 | 25 | private: 26 | QString m_roleName; 27 | }; 28 | 29 | } 30 | 31 | #endif // ROLEFILTER_H 32 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/valuefilter.h: -------------------------------------------------------------------------------- 1 | #ifndef VALUEFILTER_H 2 | #define VALUEFILTER_H 3 | 4 | #include "rolefilter.h" 5 | #include 6 | 7 | namespace qqsfpm { 8 | 9 | class ValueFilter : public RoleFilter { 10 | Q_OBJECT 11 | Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) 12 | 13 | public: 14 | using RoleFilter::RoleFilter; 15 | 16 | const QVariant& value() const; 17 | void setValue(const QVariant& value); 18 | 19 | protected: 20 | bool filterRow(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 21 | 22 | Q_SIGNALS: 23 | void valueChanged(); 24 | 25 | private: 26 | QVariant m_value; 27 | }; 28 | 29 | } 30 | 31 | #endif // VALUEFILTER_H 32 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/filtercontainerfilter.cpp: -------------------------------------------------------------------------------- 1 | #include "filtercontainerfilter.h" 2 | 3 | namespace qqsfpm { 4 | 5 | void FilterContainerFilter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) 6 | { 7 | for (Filter* filter : m_filters) 8 | filter->proxyModelCompleted(proxyModel); 9 | } 10 | 11 | void FilterContainerFilter::onFilterAppended(Filter* filter) 12 | { 13 | connect(filter, &Filter::invalidated, this, &FilterContainerFilter::invalidate); 14 | invalidate(); 15 | } 16 | 17 | void FilterContainerFilter::onFilterRemoved(Filter* filter) 18 | { 19 | Q_UNUSED(filter) 20 | invalidate(); 21 | } 22 | 23 | void qqsfpm::FilterContainerFilter::onFiltersCleared() 24 | { 25 | invalidate(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/filterrole.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTERROLE_H 2 | #define FILTERROLE_H 3 | 4 | #include "singlerole.h" 5 | #include "filters/filtercontainer.h" 6 | 7 | namespace qqsfpm { 8 | 9 | class FilterRole : public SingleRole, public FilterContainer 10 | { 11 | Q_OBJECT 12 | Q_INTERFACES(qqsfpm::FilterContainer) 13 | Q_PROPERTY(QQmlListProperty filters READ filtersListProperty) 14 | Q_CLASSINFO("DefaultProperty", "filters") 15 | 16 | public: 17 | using SingleRole::SingleRole; 18 | 19 | private: 20 | void onFilterAppended(Filter* filter) override; 21 | void onFilterRemoved(Filter* filter) override; 22 | void onFiltersCleared() override; 23 | 24 | QVariant data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) override; 25 | }; 26 | 27 | } 28 | 29 | #endif // FILTERROLE_H 30 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/singlerole.h: -------------------------------------------------------------------------------- 1 | #ifndef SINGLEROLE_H 2 | #define SINGLEROLE_H 3 | 4 | #include "proxyrole.h" 5 | 6 | namespace qqsfpm { 7 | 8 | class SingleRole : public ProxyRole 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) 12 | 13 | public: 14 | using ProxyRole::ProxyRole; 15 | 16 | QString name() const; 17 | void setName(const QString& name); 18 | 19 | QStringList names() override; 20 | 21 | Q_SIGNALS: 22 | void nameChanged(); 23 | 24 | private: 25 | QString m_name; 26 | 27 | private: 28 | QVariant data(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel &proxyModel, const QString &name) final; 29 | virtual QVariant data(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel &proxyModel) = 0; 30 | }; 31 | 32 | } 33 | 34 | #endif // SINGLEROLE_H 35 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/sorters/rolesorter.h: -------------------------------------------------------------------------------- 1 | #ifndef ROLESORTER_H 2 | #define ROLESORTER_H 3 | 4 | #include "sorter.h" 5 | 6 | namespace qqsfpm { 7 | 8 | class RoleSorter : public Sorter 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(QString roleName READ roleName WRITE setRoleName NOTIFY roleNameChanged) 12 | 13 | public: 14 | using Sorter::Sorter; 15 | 16 | const QString& roleName() const; 17 | void setRoleName(const QString& roleName); 18 | 19 | Q_SIGNALS: 20 | void roleNameChanged(); 21 | 22 | protected: 23 | QPair sourceData(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const; 24 | int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const override; 25 | 26 | private: 27 | QString m_roleName; 28 | }; 29 | 30 | } 31 | 32 | #endif // ROLESORTER_H 33 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/filtercontainerfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTERCONTAINERFILTER_H 2 | #define FILTERCONTAINERFILTER_H 3 | 4 | #include "filter.h" 5 | #include "filtercontainer.h" 6 | 7 | namespace qqsfpm { 8 | 9 | class FilterContainerFilter : public Filter, public FilterContainer { 10 | Q_OBJECT 11 | Q_INTERFACES(qqsfpm::FilterContainer) 12 | Q_PROPERTY(QQmlListProperty filters READ filtersListProperty NOTIFY filtersChanged) 13 | Q_CLASSINFO("DefaultProperty", "filters") 14 | 15 | public: 16 | using Filter::Filter; 17 | 18 | void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override; 19 | 20 | Q_SIGNALS: 21 | void filtersChanged(); 22 | 23 | private: 24 | void onFilterAppended(Filter* filter) override; 25 | void onFilterRemoved(Filter* filter) override; 26 | void onFiltersCleared() override; 27 | }; 28 | 29 | } 30 | 31 | #endif // FILTERCONTAINERFILTER_H 32 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/proxyrolesqmltypes.cpp: -------------------------------------------------------------------------------- 1 | #include "proxyrole.h" 2 | #include "joinrole.h" 3 | #include "switchrole.h" 4 | #include "expressionrole.h" 5 | #include "regexprole.h" 6 | #include "filterrole.h" 7 | #include 8 | #include 9 | 10 | namespace qqsfpm { 11 | 12 | void registerProxyRoleTypes() { 13 | qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "ProxyRole", "ProxyRole is an abstract class"); 14 | qmlRegisterType("SortFilterProxyModel", 0, 2, "JoinRole"); 15 | qmlRegisterType("SortFilterProxyModel", 0, 2, "SwitchRole"); 16 | qmlRegisterType("SortFilterProxyModel", 0, 2, "ExpressionRole"); 17 | qmlRegisterType("SortFilterProxyModel", 0, 2, "RegExpRole"); 18 | qmlRegisterType("SortFilterProxyModel", 0, 2, "FilterRole"); 19 | } 20 | 21 | Q_COREAPP_STARTUP_FUNCTION(registerProxyRoleTypes) 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/joinrole.h: -------------------------------------------------------------------------------- 1 | #ifndef JOINROLE_H 2 | #define JOINROLE_H 3 | 4 | #include "singlerole.h" 5 | 6 | namespace qqsfpm { 7 | 8 | class JoinRole : public SingleRole 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(QStringList roleNames READ roleNames WRITE setRoleNames NOTIFY roleNamesChanged) 12 | Q_PROPERTY(QString separator READ separator WRITE setSeparator NOTIFY separatorChanged) 13 | 14 | public: 15 | using SingleRole::SingleRole; 16 | 17 | QStringList roleNames() const; 18 | void setRoleNames(const QStringList& roleNames); 19 | 20 | QString separator() const; 21 | void setSeparator(const QString& separator); 22 | 23 | Q_SIGNALS: 24 | void roleNamesChanged(); 25 | 26 | void separatorChanged(); 27 | 28 | private: 29 | QStringList m_roleNames; 30 | QVariant data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) override; 31 | QString m_separator = " "; 32 | }; 33 | 34 | } 35 | 36 | #endif // JOINROLE_H 37 | -------------------------------------------------------------------------------- /nix/derivation.nix: -------------------------------------------------------------------------------- 1 | { 2 | stdenv, 3 | qtbase, 4 | qtdeclarative, 5 | qmake, 6 | wrapQtAppsHook, 7 | lib, 8 | }: 9 | stdenv.mkDerivation { 10 | pname = "CertInfo"; 11 | version = "1.0"; 12 | 13 | src = ../.; 14 | 15 | buildInputs = [qtbase qtdeclarative]; 16 | nativeBuildInputs = [wrapQtAppsHook qmake]; 17 | 18 | # Wrapping the inside of the app bundles, avoiding double-wrapping 19 | dontWrapQtApps = stdenv.hostPlatform.isDarwin; 20 | 21 | postInstall = lib.optionalString stdenv.hostPlatform.isDarwin '' 22 | mkdir -p $out/Applications 23 | mv $out/{bin,Applications}/CertInfo.app 24 | ln -s $out/{Applications/CertInfo.app/Contents/MacOS,bin}/CertInfo 25 | wrapQtApp $out/Applications/CertInfo.app/Contents/MacOS/CertInfo 26 | ''; 27 | 28 | postPatch = '' 29 | substituteInPlace src/versioncheck/versioncheck.cpp \ 30 | --replace 'VersionCheck.h' 'versioncheck.h' 31 | substituteInPlace CertInfo.pro \ 32 | --replace '/opt/$${TARGET}/bin' '$$PREFIX/bin' 33 | ''; 34 | } 35 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/proxyrole.h: -------------------------------------------------------------------------------- 1 | #ifndef PROXYROLE_H 2 | #define PROXYROLE_H 3 | 4 | #include 5 | #include 6 | 7 | namespace qqsfpm { 8 | 9 | class QQmlSortFilterProxyModel; 10 | 11 | class ProxyRole : public QObject 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | using QObject::QObject; 17 | virtual ~ProxyRole() = default; 18 | 19 | QVariant roleData(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel, const QString& name); 20 | virtual void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel); 21 | 22 | virtual QStringList names() = 0; 23 | 24 | protected: 25 | void invalidate(); 26 | 27 | Q_SIGNALS: 28 | void invalidated(); 29 | void namesAboutToBeChanged(); 30 | void namesChanged(); 31 | 32 | private: 33 | virtual QVariant data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel, const QString& name) = 0; 34 | 35 | QMutex m_mutex; 36 | }; 37 | 38 | } 39 | 40 | #endif // PROXYROLE_H 41 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/sorters/sortersqmltypes.cpp: -------------------------------------------------------------------------------- 1 | #include "sorter.h" 2 | #include "rolesorter.h" 3 | #include "stringsorter.h" 4 | #include "filtersorter.h" 5 | #include "expressionsorter.h" 6 | #include "sortercontainer.h" 7 | #include 8 | #include 9 | 10 | namespace qqsfpm { 11 | 12 | void registerSorterTypes() { 13 | qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "Sorter", "Sorter is an abstract class"); 14 | qmlRegisterType("SortFilterProxyModel", 0, 2, "RoleSorter"); 15 | qmlRegisterType("SortFilterProxyModel", 0, 2, "StringSorter"); 16 | qmlRegisterType("SortFilterProxyModel", 0, 2, "FilterSorter"); 17 | qmlRegisterType("SortFilterProxyModel", 0, 2, "ExpressionSorter"); 18 | qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "SorterContainer", "SorterContainer can only be used as an attaching type"); 19 | } 20 | 21 | Q_COREAPP_STARTUP_FUNCTION(registerSorterTypes) 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/listmodel/domaincountlistmodel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "genericlistmodel.h" 20 | #include 21 | 22 | typedef QPair QIntPair; 23 | 24 | class domainCountListModel : public GenericListModel 25 | { 26 | Q_OBJECT 27 | public: 28 | domainCountListModel(QObject* parent = nullptr); 29 | }; 30 | -------------------------------------------------------------------------------- /src/listmodel/domaincountlistmodel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | 18 | #include "domaincountlistmodel.h" 19 | 20 | domainCountListModel::domainCountListModel(QObject* parent) : GenericListModel(parent) 21 | { 22 | addSelector("domain", [](const QIntPair &m) { return m.first; }); 23 | addSelector("count", [](const QIntPair &m) { return m.second; }); 24 | } 25 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/indexfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef INDEXFILTER_H 2 | #define INDEXFILTER_H 3 | 4 | #include "filter.h" 5 | #include 6 | 7 | namespace qqsfpm { 8 | 9 | class IndexFilter: public Filter { 10 | Q_OBJECT 11 | Q_PROPERTY(QVariant minimumIndex READ minimumIndex WRITE setMinimumIndex NOTIFY minimumIndexChanged) 12 | Q_PROPERTY(QVariant maximumIndex READ maximumIndex WRITE setMaximumIndex NOTIFY maximumIndexChanged) 13 | 14 | public: 15 | using Filter::Filter; 16 | 17 | const QVariant& minimumIndex() const; 18 | void setMinimumIndex(const QVariant& minimumIndex); 19 | 20 | const QVariant& maximumIndex() const; 21 | void setMaximumIndex(const QVariant& maximumIndex); 22 | 23 | protected: 24 | bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 25 | 26 | Q_SIGNALS: 27 | void minimumIndexChanged(); 28 | void maximumIndexChanged(); 29 | 30 | private: 31 | QVariant m_minimumIndex; 32 | QVariant m_maximumIndex; 33 | }; 34 | 35 | } 36 | 37 | #endif // INDEXFILTER_H 38 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Pierre-Yves Siret 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/sorters/filtersorter.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTERSORTER_H 2 | #define FILTERSORTER_H 3 | 4 | #include "sorter.h" 5 | #include "filters/filtercontainer.h" 6 | 7 | namespace qqsfpm { 8 | 9 | class FilterSorter : public Sorter, public FilterContainer 10 | { 11 | Q_OBJECT 12 | Q_INTERFACES(qqsfpm::FilterContainer) 13 | Q_PROPERTY(QQmlListProperty filters READ filtersListProperty) 14 | Q_CLASSINFO("DefaultProperty", "filters") 15 | 16 | public: 17 | using Sorter::Sorter; 18 | 19 | protected: 20 | int compare(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const QQmlSortFilterProxyModel &proxyModel) const override; 21 | 22 | private: 23 | void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override; 24 | void onFilterAppended(Filter *filter) override; 25 | void onFilterRemoved(Filter *filter) override; 26 | void onFiltersCleared() override; 27 | 28 | bool indexIsAccepted(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel &proxyModel) const; 29 | }; 30 | 31 | } 32 | 33 | #endif // FILTERSORTER_H 34 | -------------------------------------------------------------------------------- /src/+qt6/ImportHostsFileDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Dialogs 3 | 4 | FileDialog { 5 | property bool isFirefox: false 6 | property bool isTextFile: false 7 | required property var db 8 | required property var txt 9 | id: root 10 | title: isTextFile ? "Choose a txt file with one domain per line" : isFirefox ? "Please choose the Firefox places.sqlite file" : "Please choose the Chrome/Edge History file" 11 | nameFilters: isTextFile ? [ "text file (*.txt)", "All files (*)" ] : isFirefox ? [ "places.sqlite (places.sqlite)", "All files (*)" ] : [ "History (History)", "All files (*)" ] 12 | onAccepted: { 13 | txt.hostnames = "" 14 | db.hostnames = "" 15 | if(root.isTextFile) { 16 | txt.getHostnamesFromTextFile(root.selectedFile) 17 | } else { 18 | if(!db.openDb(root.selectedFile)) { 19 | infoText.text = "Error opening db file!" 20 | } else { 21 | db.isFirefox = root.isFirefox; 22 | db.getHostnamesFromDb(); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/+qt5/ImportHostsFileDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Dialogs 1.3 3 | 4 | FileDialog { 5 | property bool isFirefox: false 6 | property bool isTextFile: false 7 | required property var db 8 | required property var txt 9 | id: root 10 | title: isTextFile ? "Choose a txt file with one domain per line" : isFirefox ? "Please choose the Firefox places.sqlite file" : "Please choose the Chrome/Edge History file" 11 | nameFilters: isTextFile ? [ "text file (*.txt)", "All files (*)" ] : isFirefox ? [ "places.sqlite (places.sqlite)", "All files (*)" ] : [ "History (History)", "All files (*)" ] 12 | folder: shortcuts.home 13 | onAccepted: { 14 | txt.hostnames = "" 15 | db.hostnames = "" 16 | if(root.isTextFile) { 17 | txt.getHostnamesFromTextFile(root.fileUrl) 18 | } else { 19 | if(!db.openDb(root.fileUrl)) { 20 | infoText.text = "Error opening db file!" 21 | } else { 22 | db.isFirefox = root.isFirefox; 23 | db.getHostnamesFromDb(); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/alloffilter.cpp: -------------------------------------------------------------------------------- 1 | #include "alloffilter.h" 2 | 3 | namespace qqsfpm { 4 | 5 | /*! 6 | \qmltype AllOf 7 | \inherits Filter 8 | \inqmlmodule SortFilterProxyModel 9 | \ingroup Filters 10 | \ingroup FilterContainer 11 | \brief Filter container accepting rows accepted by all its child filters. 12 | 13 | The AllOf type is a \l Filter container that accepts rows if all of its contained (and enabled) filters accept them, or if it has no filter. 14 | 15 | Using it as a top level filter has the same effect as putting all its child filters as top level filters. It can however be usefull to use an AllOf filter when nested in an AnyOf filter. 16 | \sa FilterContainer 17 | */ 18 | bool AllOfFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 19 | { 20 | //return true if all filters return false, or if there is no filter. 21 | return std::all_of(m_filters.begin(), m_filters.end(), 22 | [&sourceIndex, &proxyModel] (Filter* filter) { 23 | return filter->filterAcceptsRow(sourceIndex, proxyModel); 24 | } 25 | ); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/expressionrole.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPRESSIONROLE_H 2 | #define EXPRESSIONROLE_H 3 | 4 | #include "singlerole.h" 5 | #include 6 | 7 | class QQmlExpression; 8 | 9 | namespace qqsfpm { 10 | 11 | class ExpressionRole : public SingleRole 12 | { 13 | Q_OBJECT 14 | Q_PROPERTY(QQmlScriptString expression READ expression WRITE setExpression NOTIFY expressionChanged) 15 | 16 | public: 17 | using SingleRole::SingleRole; 18 | 19 | const QQmlScriptString& expression() const; 20 | void setExpression(const QQmlScriptString& scriptString); 21 | 22 | void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override; 23 | 24 | Q_SIGNALS: 25 | void expressionChanged(); 26 | 27 | private: 28 | QVariant data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) override; 29 | void updateContext(const QQmlSortFilterProxyModel& proxyModel); 30 | void updateExpression(); 31 | 32 | QQmlScriptString m_scriptString; 33 | QQmlExpression* m_expression = nullptr; 34 | QQmlContext* m_context = nullptr; 35 | }; 36 | 37 | } 38 | 39 | #endif // EXPRESSIONROLE_H 40 | -------------------------------------------------------------------------------- /src/listmodel/caissuerlistmodel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "src/ca/certificate.h" 20 | #include "genericlistmodel.h" 21 | #include 22 | #include 23 | 24 | class CACertificateListModel : public GenericListModel 25 | { 26 | Q_OBJECT 27 | public: 28 | CACertificateListModel(QObject* parent = nullptr); 29 | void addOrUpdateItem(const Certificate& itemToAdd, const QString& findBySubject); 30 | }; 31 | 32 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/expressionfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPRESSIONFILTER_H 2 | #define EXPRESSIONFILTER_H 3 | 4 | #include "filter.h" 5 | #include 6 | 7 | class QQmlExpression; 8 | 9 | namespace qqsfpm { 10 | 11 | class ExpressionFilter : public Filter 12 | { 13 | Q_OBJECT 14 | Q_PROPERTY(QQmlScriptString expression READ expression WRITE setExpression NOTIFY expressionChanged) 15 | 16 | public: 17 | using Filter::Filter; 18 | 19 | const QQmlScriptString& expression() const; 20 | void setExpression(const QQmlScriptString& scriptString); 21 | 22 | void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override; 23 | 24 | protected: 25 | bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 26 | 27 | Q_SIGNALS: 28 | void expressionChanged(); 29 | 30 | private: 31 | void updateContext(const QQmlSortFilterProxyModel& proxyModel); 32 | void updateExpression(); 33 | 34 | QQmlScriptString m_scriptString; 35 | QQmlExpression* m_expression = nullptr; 36 | QQmlContext* m_context = nullptr; 37 | }; 38 | 39 | } 40 | 41 | #endif // EXPRESSIONFILTER_H 42 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/regexpfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef REGEXPFILTER_H 2 | #define REGEXPFILTER_H 3 | 4 | #include "rolefilter.h" 5 | 6 | #include 7 | 8 | namespace qqsfpm { 9 | 10 | class RegExpFilter : public RoleFilter { 11 | Q_OBJECT 12 | Q_PROPERTY(QString pattern READ pattern WRITE setPattern NOTIFY patternChanged) 13 | Q_PROPERTY(Qt::CaseSensitivity caseSensitivity READ caseSensitivity WRITE setCaseSensitivity NOTIFY caseSensitivityChanged) 14 | 15 | public: 16 | using RoleFilter::RoleFilter; 17 | 18 | RegExpFilter(); 19 | 20 | QString pattern() const; 21 | void setPattern(const QString& pattern); 22 | 23 | Qt::CaseSensitivity caseSensitivity() const; 24 | void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity); 25 | 26 | protected: 27 | bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 28 | 29 | Q_SIGNALS: 30 | void patternChanged(); 31 | void caseSensitivityChanged(); 32 | 33 | private: 34 | QRegularExpression m_regExp; 35 | Qt::CaseSensitivity m_caseSensitivity; 36 | QString m_pattern = m_regExp.pattern(); 37 | }; 38 | 39 | } 40 | 41 | #endif // REGEXPFILTER_H 42 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/sorters/expressionsorter.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPRESSIONSORTER_H 2 | #define EXPRESSIONSORTER_H 3 | 4 | #include "sorter.h" 5 | #include 6 | 7 | class QQmlExpression; 8 | 9 | namespace qqsfpm { 10 | 11 | class QQmlSortFilterProxyModel; 12 | 13 | class ExpressionSorter : public Sorter 14 | { 15 | Q_OBJECT 16 | Q_PROPERTY(QQmlScriptString expression READ expression WRITE setExpression NOTIFY expressionChanged) 17 | 18 | public: 19 | using Sorter::Sorter; 20 | 21 | const QQmlScriptString& expression() const; 22 | void setExpression(const QQmlScriptString& scriptString); 23 | 24 | void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override; 25 | 26 | Q_SIGNALS: 27 | void expressionChanged(); 28 | 29 | protected: 30 | int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const override; 31 | 32 | private: 33 | void updateContext(const QQmlSortFilterProxyModel& proxyModel); 34 | void updateExpression(); 35 | 36 | QQmlScriptString m_scriptString; 37 | QQmlExpression* m_expression = nullptr; 38 | QQmlContext* m_context = nullptr; 39 | }; 40 | 41 | } 42 | 43 | #endif // EXPRESSIONSORTER_H 44 | -------------------------------------------------------------------------------- /.github/workflows/qmake.yml: -------------------------------------------------------------------------------- 1 | # This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage. 2 | # See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml 3 | name: CMake on a single platform 4 | 5 | on: 6 | push: 7 | branches: [ "master", "citest" ] 8 | pull_request: 9 | branches: [ "master", "citest" ] 10 | 11 | env: 12 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 13 | BUILD_TYPE: Release 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | 22 | - name: install qt 23 | run: sudo apt-get update && sudo apt-get -qy install qtdeclarative5-dev qtbase5-dev qt5-qmake qtquickcontrols2-5-dev software-properties-common qttools5-dev-tools qtbase5-dev libqt5svg5-dev qtdeclarative5-dev-tools qml-module-qtquick-controls 24 | 25 | - name: Build folder 26 | run: mkdir -p build 27 | 28 | - name: Configure QMake 29 | working-directory: ${{github.workspace}}/build 30 | run: qmake ../CertInfo.pro CONFIG+=qtquickcompiler 31 | 32 | - name: Build 33 | working-directory: ${{github.workspace}}/build 34 | run: make -j 35 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/filter.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTER_H 2 | #define FILTER_H 3 | 4 | #include 5 | 6 | namespace qqsfpm { 7 | 8 | class QQmlSortFilterProxyModel; 9 | 10 | class Filter : public QObject 11 | { 12 | Q_OBJECT 13 | Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) 14 | Q_PROPERTY(bool inverted READ inverted WRITE setInverted NOTIFY invertedChanged) 15 | 16 | public: 17 | explicit Filter(QObject *parent = nullptr); 18 | virtual ~Filter() = default; 19 | 20 | bool enabled() const; 21 | void setEnabled(bool enabled); 22 | 23 | bool inverted() const; 24 | void setInverted(bool inverted); 25 | 26 | bool filterAcceptsRow(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const; 27 | 28 | virtual void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel); 29 | 30 | Q_SIGNALS: 31 | void enabledChanged(); 32 | void invertedChanged(); 33 | void invalidated(); 34 | 35 | protected: 36 | virtual bool filterRow(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const = 0; 37 | void invalidate(); 38 | 39 | private: 40 | bool m_enabled = true; 41 | bool m_inverted = false; 42 | }; 43 | 44 | } 45 | 46 | #endif // FILTER_H 47 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/filtersqmltypes.cpp: -------------------------------------------------------------------------------- 1 | #include "filter.h" 2 | #include "valuefilter.h" 3 | #include "indexfilter.h" 4 | #include "regexpfilter.h" 5 | #include "rangefilter.h" 6 | #include "expressionfilter.h" 7 | #include "anyoffilter.h" 8 | #include "alloffilter.h" 9 | #include 10 | #include 11 | 12 | namespace qqsfpm { 13 | 14 | void registerFiltersTypes() { 15 | qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "Filter", "Filter is an abstract class"); 16 | qmlRegisterType("SortFilterProxyModel", 0, 2, "ValueFilter"); 17 | qmlRegisterType("SortFilterProxyModel", 0, 2, "IndexFilter"); 18 | qmlRegisterType("SortFilterProxyModel", 0, 2, "RegExpFilter"); 19 | qmlRegisterType("SortFilterProxyModel", 0, 2, "RangeFilter"); 20 | qmlRegisterType("SortFilterProxyModel", 0, 2, "ExpressionFilter"); 21 | qmlRegisterType("SortFilterProxyModel", 0, 2, "AnyOf"); 22 | qmlRegisterType("SortFilterProxyModel", 0, 2, "AllOf"); 23 | qmlRegisterUncreatableType("SortFilterProxyModel", 0, 2, "FilterContainer", "FilterContainer can only be used as an attaching type"); 24 | } 25 | 26 | Q_COREAPP_STARTUP_FUNCTION(registerFiltersTypes) 27 | 28 | } 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | *.sqlite 3 | history 4 | History 5 | # ---> Qt 6 | # C++ objects and libs 7 | *.slo 8 | *.lo 9 | *.o 10 | *.a 11 | *.la 12 | *.lai 13 | *.so 14 | *.so.* 15 | *.dll 16 | *.dylib 17 | 18 | # Qt-es 19 | object_script.*.Release 20 | object_script.*.Debug 21 | *_plugin_import.cpp 22 | /.qmake.cache 23 | /.qmake.stash 24 | *.pro.user 25 | *.pro.user.* 26 | *.qbs.user 27 | *.qbs.user.* 28 | *.moc 29 | moc_*.cpp 30 | moc_*.h 31 | qrc_*.cpp 32 | ui_*.h 33 | *.qmlc 34 | *.jsc 35 | Makefile* 36 | *build-* 37 | *.qm 38 | *.prl 39 | 40 | # Qt unit tests 41 | target_wrapper.* 42 | 43 | # QtCreator 44 | *.autosave 45 | 46 | # QtCreator Qml 47 | *.qmlproject.user 48 | *.qmlproject.user.* 49 | 50 | # QtCreator CMake 51 | CMakeLists.txt.user* 52 | 53 | # QtCreator 4.8< compilation database 54 | compile_commands.json 55 | 56 | # QtCreator local machine specific files for imported projects 57 | *creator.user* 58 | 59 | *_qmlcache.qrc 60 | 61 | # ---> C++ 62 | # Prerequisites 63 | *.d 64 | 65 | # Compiled Object files 66 | *.slo 67 | *.lo 68 | *.o 69 | *.obj 70 | 71 | # Precompiled Headers 72 | *.gch 73 | *.pch 74 | 75 | # Compiled Dynamic libraries 76 | *.so 77 | *.dylib 78 | *.dll 79 | 80 | # Fortran module files 81 | *.mod 82 | *.smod 83 | 84 | # Compiled Static libraries 85 | *.lai 86 | *.la 87 | *.a 88 | *.lib 89 | 90 | # Executables 91 | *.exe 92 | *.out 93 | *.app 94 | 95 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/regexprole.h: -------------------------------------------------------------------------------- 1 | #ifndef REGEXPROLE_H 2 | #define REGEXPROLE_H 3 | 4 | #include "proxyrole.h" 5 | #include 6 | 7 | namespace qqsfpm { 8 | 9 | class RegExpRole : public ProxyRole 10 | { 11 | Q_OBJECT 12 | Q_PROPERTY(QString roleName READ roleName WRITE setRoleName NOTIFY roleNameChanged) 13 | Q_PROPERTY(QString pattern READ pattern WRITE setPattern NOTIFY patternChanged) 14 | Q_PROPERTY(Qt::CaseSensitivity caseSensitivity READ caseSensitivity WRITE setCaseSensitivity NOTIFY caseSensitivityChanged) 15 | 16 | public: 17 | using ProxyRole::ProxyRole; 18 | 19 | QString roleName() const; 20 | void setRoleName(const QString& roleName); 21 | 22 | QString pattern() const; 23 | void setPattern(const QString& pattern); 24 | 25 | Qt::CaseSensitivity caseSensitivity() const; 26 | void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity); 27 | 28 | QStringList names() override; 29 | 30 | Q_SIGNALS: 31 | void roleNameChanged(); 32 | void patternChanged(); 33 | void caseSensitivityChanged(); 34 | 35 | private: 36 | QString m_roleName; 37 | QRegularExpression m_regularExpression; 38 | QVariant data(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel &proxyModel, const QString &name) override; 39 | }; 40 | 41 | } 42 | 43 | #endif // REGEXPROLE_H 44 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/rolefilter.cpp: -------------------------------------------------------------------------------- 1 | #include "rolefilter.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype RoleFilter 8 | \qmlabstract 9 | \inherits Filter 10 | \inqmlmodule SortFilterProxyModel 11 | \ingroup Filters 12 | \brief Base type for filters based on a source model role. 13 | 14 | The RoleFilter type cannot be used directly in a QML file. 15 | It exists to provide a set of common properties and methods, 16 | available across all the other filter types that inherit from it. 17 | Attempting to use the RoleFilter type directly will result in an error. 18 | */ 19 | 20 | /*! 21 | \qmlproperty string RoleFilter::roleName 22 | 23 | This property holds the role name that the filter is using to query the source model's data when filtering items. 24 | */ 25 | const QString& RoleFilter::roleName() const 26 | { 27 | return m_roleName; 28 | } 29 | 30 | void RoleFilter::setRoleName(const QString& roleName) 31 | { 32 | if (m_roleName == roleName) 33 | return; 34 | 35 | m_roleName = roleName; 36 | Q_EMIT roleNameChanged(); 37 | invalidate(); 38 | } 39 | 40 | QVariant RoleFilter::sourceData(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 41 | { 42 | return proxyModel.sourceData(sourceIndex, m_roleName); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/proxyrole.cpp: -------------------------------------------------------------------------------- 1 | #include "proxyrole.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "filters/filter.h" 9 | #include "qqmlsortfilterproxymodel.h" 10 | 11 | namespace qqsfpm { 12 | 13 | /*! 14 | \qmltype ProxyRole 15 | \inqmlmodule SortFilterProxyModel 16 | \ingroup ProxyRoles 17 | \brief Base type for the \l SortFilterProxyModel proxy roles. 18 | 19 | The ProxyRole type cannot be used directly in a QML file. 20 | It exists to provide a set of common properties and methods, 21 | available across all the other proxy role types that inherit from it. 22 | Attempting to use the ProxyRole type directly will result in an error. 23 | */ 24 | 25 | QVariant ProxyRole::roleData(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel, const QString &name) 26 | { 27 | if (m_mutex.tryLock()) { 28 | QVariant result = data(sourceIndex, proxyModel, name); 29 | m_mutex.unlock(); 30 | return result; 31 | } else { 32 | return {}; 33 | } 34 | } 35 | 36 | void ProxyRole::proxyModelCompleted(const QQmlSortFilterProxyModel &proxyModel) 37 | { 38 | Q_UNUSED(proxyModel) 39 | } 40 | 41 | void ProxyRole::invalidate() 42 | { 43 | Q_EMIT invalidated(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/singlerole.cpp: -------------------------------------------------------------------------------- 1 | #include "singlerole.h" 2 | #include 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype SingleRole 8 | \qmlabstract 9 | \inherits ProxyRole 10 | \inqmlmodule SortFilterProxyModel 11 | \ingroup ProxyRoles 12 | \brief Base type for the \l SortFilterProxyModel proxy roles defining a single role. 13 | 14 | SingleRole is a convenience base class for proxy roles who define a single role. 15 | It cannot be used directly in a QML file. 16 | It exists to provide a set of common properties and methods, 17 | available across all the other proxy role types that inherit from it. 18 | Attempting to use the SingleRole type directly will result in an error. 19 | */ 20 | /*! 21 | \qmlproperty string SingleRole::name 22 | 23 | This property holds the role name of the proxy role. 24 | */ 25 | QString SingleRole::name() const 26 | { 27 | return m_name; 28 | } 29 | 30 | void SingleRole::setName(const QString& name) 31 | { 32 | if (m_name == name) 33 | return; 34 | 35 | Q_EMIT namesAboutToBeChanged(); 36 | m_name = name; 37 | Q_EMIT nameChanged(); 38 | Q_EMIT namesChanged(); 39 | } 40 | 41 | QStringList SingleRole::names() 42 | { 43 | return QStringList { m_name }; 44 | } 45 | 46 | QVariant SingleRole::data(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel &proxyModel, const QString &name) 47 | { 48 | Q_UNUSED(name); 49 | return data(sourceIndex, proxyModel); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/sorters/stringsorter.h: -------------------------------------------------------------------------------- 1 | #ifndef STRINGSORTER_H 2 | #define STRINGSORTER_H 3 | 4 | #include "rolesorter.h" 5 | #include 6 | 7 | namespace qqsfpm { 8 | 9 | class StringSorter : public RoleSorter 10 | { 11 | Q_OBJECT 12 | Q_PROPERTY(Qt::CaseSensitivity caseSensitivity READ caseSensitivity WRITE setCaseSensitivity NOTIFY caseSensitivityChanged) 13 | Q_PROPERTY(bool ignorePunctation READ ignorePunctation WRITE setIgnorePunctation NOTIFY ignorePunctationChanged) 14 | Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged) 15 | Q_PROPERTY(bool numericMode READ numericMode WRITE setNumericMode NOTIFY numericModeChanged) 16 | 17 | public: 18 | using RoleSorter::RoleSorter; 19 | 20 | Qt::CaseSensitivity caseSensitivity() const; 21 | void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity); 22 | 23 | bool ignorePunctation() const; 24 | void setIgnorePunctation(bool ignorePunctation); 25 | 26 | QLocale locale() const; 27 | void setLocale(const QLocale& locale); 28 | 29 | bool numericMode() const; 30 | void setNumericMode(bool numericMode); 31 | 32 | Q_SIGNALS: 33 | void caseSensitivityChanged(); 34 | void ignorePunctationChanged(); 35 | void localeChanged(); 36 | void numericModeChanged(); 37 | 38 | protected: 39 | int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const override; 40 | 41 | private: 42 | QCollator m_collator; 43 | }; 44 | 45 | } 46 | 47 | #endif // STRINGSORTER_H 48 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/proxyrolecontainer.h: -------------------------------------------------------------------------------- 1 | #ifndef PROXYROLECONTAINER_H 2 | #define PROXYROLECONTAINER_H 3 | 4 | #include 5 | #include 6 | 7 | namespace qqsfpm { 8 | 9 | class ProxyRole; 10 | class QQmlSortFilterProxyModel; 11 | 12 | class ProxyRoleContainer { 13 | public: 14 | virtual ~ProxyRoleContainer() = default; 15 | 16 | QList proxyRoles() const; 17 | void appendProxyRole(ProxyRole* proxyRole); 18 | void removeProxyRole(ProxyRole* proxyRole); 19 | void clearProxyRoles(); 20 | 21 | QQmlListProperty proxyRolesListProperty(); 22 | 23 | protected: 24 | QList m_proxyRoles; 25 | 26 | private: 27 | virtual void onProxyRoleAppended(ProxyRole* proxyRole) = 0; 28 | virtual void onProxyRoleRemoved(ProxyRole* proxyRole) = 0; 29 | virtual void onProxyRolesCleared() = 0; 30 | 31 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 32 | using sizetype = int; 33 | #else 34 | using sizetype = qsizetype; 35 | #endif 36 | 37 | static void append_proxyRole(QQmlListProperty* list, ProxyRole* proxyRole); 38 | static sizetype count_proxyRole(QQmlListProperty* list); 39 | static ProxyRole* at_proxyRole(QQmlListProperty* list, sizetype index); 40 | static void clear_proxyRoles(QQmlListProperty* list); 41 | }; 42 | 43 | } 44 | 45 | #define ProxyRoleContainer_iid "fr.grecko.SortFilterProxyModel.ProxyRoleContainer" 46 | Q_DECLARE_INTERFACE(qqsfpm::ProxyRoleContainer, ProxyRoleContainer_iid) 47 | 48 | #endif // PROXYROLECONTAINER_H 49 | -------------------------------------------------------------------------------- /src/ca/caprocessor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "certificate.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | class QNetworkReply; 26 | 27 | class CAProcessor : public QObject 28 | { 29 | Q_OBJECT 30 | public: 31 | explicit CAProcessor(QObject *parent = nullptr); 32 | 33 | static QList getCertificate(const QString& domain); 34 | static bool isCA(const QSslCertificate& cert); 35 | 36 | static Certificate parseQSslCertificateToCertificate(const QSslCertificate& cert); 37 | 38 | signals: 39 | 40 | private slots: 41 | static void ignoreSslErrors(QNetworkReply* reply); 42 | 43 | private: 44 | static void extractSubject(const QSslCertificate& cert, Certificate& out_issuer); 45 | static void extractCertificate(const QSslCertificate& cert, Certificate& out_issuer); 46 | }; 47 | 48 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/rangefilter.h: -------------------------------------------------------------------------------- 1 | #ifndef RANGEFILTER_H 2 | #define RANGEFILTER_H 3 | 4 | #include "rolefilter.h" 5 | #include 6 | 7 | namespace qqsfpm { 8 | 9 | class RangeFilter : public RoleFilter 10 | { 11 | Q_OBJECT 12 | Q_PROPERTY(QVariant minimumValue READ minimumValue WRITE setMinimumValue NOTIFY minimumValueChanged) 13 | Q_PROPERTY(bool minimumInclusive READ minimumInclusive WRITE setMinimumInclusive NOTIFY minimumInclusiveChanged) 14 | Q_PROPERTY(QVariant maximumValue READ maximumValue WRITE setMaximumValue NOTIFY maximumValueChanged) 15 | Q_PROPERTY(bool maximumInclusive READ maximumInclusive WRITE setMaximumInclusive NOTIFY maximumInclusiveChanged) 16 | 17 | public: 18 | using RoleFilter::RoleFilter; 19 | 20 | QVariant minimumValue() const; 21 | void setMinimumValue(QVariant minimumValue); 22 | bool minimumInclusive() const; 23 | void setMinimumInclusive(bool minimumInclusive); 24 | 25 | QVariant maximumValue() const; 26 | void setMaximumValue(QVariant maximumValue); 27 | bool maximumInclusive() const; 28 | void setMaximumInclusive(bool maximumInclusive); 29 | 30 | protected: 31 | bool filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const override; 32 | 33 | Q_SIGNALS: 34 | void minimumValueChanged(); 35 | void minimumInclusiveChanged(); 36 | void maximumValueChanged(); 37 | void maximumInclusiveChanged(); 38 | 39 | private: 40 | QVariant m_minimumValue; 41 | bool m_minimumInclusive = true; 42 | QVariant m_maximumValue; 43 | bool m_maximumInclusive = true; 44 | }; 45 | 46 | } 47 | 48 | #endif // RANGEFILTER_H 49 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/anyoffilter.cpp: -------------------------------------------------------------------------------- 1 | #include "anyoffilter.h" 2 | 3 | namespace qqsfpm { 4 | 5 | /*! 6 | \qmltype AnyOf 7 | \inherits Filter 8 | \inqmlmodule SortFilterProxyModel 9 | \ingroup Filters 10 | \ingroup FilterContainer 11 | \brief Filter container accepting rows accepted by at least one of its child filters. 12 | 13 | The AnyOf type is a \l Filter container that accepts rows if any of its contained (and enabled) filters accept them. 14 | 15 | In the following example, only the rows where the \c firstName role or the \c lastName role match the text entered in the \c nameTextField will be accepted : 16 | \code 17 | TextField { 18 | id: nameTextField 19 | } 20 | 21 | SortFilterProxyModel { 22 | sourceModel: contactModel 23 | filters: AnyOf { 24 | RegExpFilter { 25 | roleName: "lastName" 26 | pattern: nameTextField.text 27 | caseSensitivity: Qt.CaseInsensitive 28 | } 29 | RegExpFilter { 30 | roleName: "firstName" 31 | pattern: nameTextField.text 32 | caseSensitivity: Qt.CaseInsensitive 33 | } 34 | } 35 | } 36 | \endcode 37 | \sa FilterContainer 38 | */ 39 | bool AnyOfFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 40 | { 41 | //return true if any of the enabled filters return true 42 | return std::any_of(m_filters.begin(), m_filters.end(), 43 | [&sourceIndex, &proxyModel] (Filter* filter) { 44 | return filter->enabled() && filter->filterAcceptsRow(sourceIndex, proxyModel); 45 | } 46 | ); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1681202837, 9 | "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "cfacdce06f30d2b68473a46042957675eebb3401", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1704008649, 24 | "narHash": "sha256-rGPSWjXTXTurQN9beuHdyJhB8O761w1Zc5BqSSmHvoM=", 25 | "owner": "NixOS", 26 | "repo": "nixpkgs", 27 | "rev": "d44d59d2b5bd694cd9d996fd8c51d03e3e9ba7f7", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "NixOS", 32 | "ref": "nixpkgs-unstable", 33 | "repo": "nixpkgs", 34 | "type": "github" 35 | } 36 | }, 37 | "root": { 38 | "inputs": { 39 | "flake-utils": "flake-utils", 40 | "nixpkgs": "nixpkgs" 41 | } 42 | }, 43 | "systems": { 44 | "locked": { 45 | "lastModified": 1681028828, 46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 47 | "owner": "nix-systems", 48 | "repo": "default", 49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 50 | "type": "github" 51 | }, 52 | "original": { 53 | "owner": "nix-systems", 54 | "repo": "default", 55 | "type": "github" 56 | } 57 | } 58 | }, 59 | "root": "root", 60 | "version": 7 61 | } 62 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/sorters/sorter.h: -------------------------------------------------------------------------------- 1 | #ifndef SORTER_H 2 | #define SORTER_H 3 | 4 | #include 5 | 6 | namespace qqsfpm { 7 | 8 | class QQmlSortFilterProxyModel; 9 | 10 | class Sorter : public QObject 11 | { 12 | Q_OBJECT 13 | Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) 14 | Q_PROPERTY(bool ascendingOrder READ ascendingOrder WRITE setAscendingOrder NOTIFY sortOrderChanged) 15 | Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder NOTIFY sortOrderChanged) 16 | Q_PROPERTY(int priority READ priority WRITE setPriority NOTIFY priorityChanged) 17 | 18 | public: 19 | Sorter(QObject* parent = nullptr); 20 | virtual ~Sorter() = 0; 21 | 22 | bool enabled() const; 23 | void setEnabled(bool enabled); 24 | 25 | bool ascendingOrder() const; 26 | void setAscendingOrder(bool ascendingOrder); 27 | 28 | Qt::SortOrder sortOrder() const; 29 | void setSortOrder(Qt::SortOrder sortOrder); 30 | 31 | int priority() const; 32 | void setPriority(int priority); 33 | 34 | int compareRows(const QModelIndex& source_left, const QModelIndex& source_right, const QQmlSortFilterProxyModel& proxyModel) const; 35 | 36 | virtual void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel); 37 | 38 | Q_SIGNALS: 39 | void enabledChanged(); 40 | void sortOrderChanged(); 41 | void priorityChanged(); 42 | 43 | void invalidated(); 44 | 45 | protected: 46 | virtual int compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const; 47 | virtual bool lessThan(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const; 48 | void invalidate(); 49 | 50 | private: 51 | bool m_enabled = true; 52 | Qt::SortOrder m_sortOrder = Qt::AscendingOrder; 53 | int m_priority = 0; 54 | }; 55 | 56 | } 57 | 58 | #endif // SORTER_H 59 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/filterrole.cpp: -------------------------------------------------------------------------------- 1 | #include "filterrole.h" 2 | #include "filters/filter.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype FilterRole 8 | \inherits SingleRole 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup ProxyRoles 11 | \ingroup FilterContainer 12 | \brief A role resolving to \c true for rows matching all its filters. 13 | 14 | A FilterRole is a \l ProxyRole that returns \c true for rows matching all its filters. 15 | 16 | In the following example, the \c isAdult role will be equal to \c true if the \c age role is superior or equal to 18. 17 | \code 18 | SortFilterProxyModel { 19 | sourceModel: personModel 20 | proxyRoles: FilterRole { 21 | name: "isAdult" 22 | RangeFilter { roleName: "age"; minimumValue: 18; minimumInclusive: true } 23 | } 24 | } 25 | \endcode 26 | \sa FilterContainer 27 | */ 28 | 29 | /*! 30 | \qmlproperty list FilterRole::filters 31 | \default 32 | 33 | This property holds the list of filters for this filter role. 34 | The data of this role will be equal to the \c true if all its filters match the model row, \c false otherwise. 35 | 36 | \sa Filter, FilterContainer 37 | */ 38 | 39 | void FilterRole::onFilterAppended(Filter* filter) 40 | { 41 | connect(filter, &Filter::invalidated, this, &FilterRole::invalidate); 42 | invalidate(); 43 | } 44 | 45 | void FilterRole::onFilterRemoved(Filter* filter) 46 | { 47 | disconnect(filter, &Filter::invalidated, this, &FilterRole::invalidate); 48 | invalidate(); 49 | } 50 | 51 | void FilterRole::onFiltersCleared() 52 | { 53 | invalidate(); 54 | } 55 | 56 | QVariant FilterRole::data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) 57 | { 58 | return std::all_of(m_filters.begin(), m_filters.end(), 59 | [&] (Filter* filter) { 60 | return filter->filterAcceptsRow(sourceIndex, proxyModel); 61 | } 62 | ); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/valuefilter.cpp: -------------------------------------------------------------------------------- 1 | #include "valuefilter.h" 2 | 3 | namespace qqsfpm { 4 | 5 | /*! 6 | \qmltype ValueFilter 7 | \inherits RoleFilter 8 | \inqmlmodule SortFilterProxyModel 9 | \ingroup Filters 10 | \brief Filters rows matching exactly a value. 11 | 12 | A ValueFilter is a simple \l RoleFilter that accepts rows matching exactly the filter's value 13 | 14 | In the following example, only rows with their \c favorite role set to \c true will be accepted when the checkbox is checked : 15 | \code 16 | CheckBox { 17 | id: showOnlyFavoriteCheckBox 18 | } 19 | 20 | SortFilterProxyModel { 21 | sourceModel: contactModel 22 | filters: ValueFilter { 23 | roleName: "favorite" 24 | value: true 25 | enabled: showOnlyFavoriteCheckBox.checked 26 | } 27 | } 28 | \endcode 29 | 30 | */ 31 | 32 | /*! 33 | \qmlproperty variant ValueFilter::value 34 | 35 | This property holds the value used to filter the contents of the source model. 36 | */ 37 | const QVariant &ValueFilter::value() const 38 | { 39 | return m_value; 40 | } 41 | 42 | void ValueFilter::setValue(const QVariant& value) 43 | { 44 | if (m_value == value) 45 | return; 46 | 47 | m_value = value; 48 | Q_EMIT valueChanged(); 49 | invalidate(); 50 | } 51 | 52 | bool ValueFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 53 | { 54 | QVariant srcData = sourceData(sourceIndex, proxyModel); 55 | #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) 56 | // Implicitly convert the types. This was the behavior in Qt5 and makes QML 57 | // interop much easier, e.g. when comparing QByteArray against QString 58 | if (srcData.metaType() != m_value.metaType()) { 59 | QVariant converted = srcData; 60 | if (converted.convert(m_value.metaType())) { 61 | srcData = converted; 62 | } 63 | } 64 | #endif 65 | return !m_value.isValid() || m_value == srcData; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/listmodel/qabstractlistmodelwithrowcountsignal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | #pragma once 17 | 18 | #include 19 | 20 | 21 | class QAbstractListModelWithRowCountSignal : public QAbstractListModel 22 | { 23 | Q_OBJECT 24 | Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged) 25 | 26 | public: 27 | virtual ~QAbstractListModelWithRowCountSignal() = default; 28 | 29 | protected: 30 | QAbstractListModelWithRowCountSignal(QObject *parent = nullptr) : 31 | QAbstractListModel(parent) 32 | { 33 | QObject::connect(this, &QAbstractListModel::rowsInserted, this, &QAbstractListModelWithRowCountSignal::rowCountChanged); 34 | QObject::connect(this, &QAbstractListModel::rowsRemoved, this, &QAbstractListModelWithRowCountSignal::rowCountChanged); 35 | QObject::connect(this, &QAbstractListModel::modelReset, this, &QAbstractListModelWithRowCountSignal::rowCountChanged); 36 | QObject::connect(this, &QAbstractListModel::layoutChanged, this, &QAbstractListModelWithRowCountSignal::rowCountChanged); 37 | QObject::connect(this, &QAbstractListModel::rowsMoved, this, &QAbstractListModelWithRowCountSignal::rowCountChanged); 38 | QObject::connect(this, &QAbstractListModel::dataChanged, this, &QAbstractListModelWithRowCountSignal::rowCountChanged); 39 | } 40 | 41 | signals: 42 | void rowCountChanged(); 43 | }; 44 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/switchrole.h: -------------------------------------------------------------------------------- 1 | #ifndef SWITCHROLE_H 2 | #define SWITCHROLE_H 3 | 4 | #include "singlerole.h" 5 | #include "filters/filtercontainer.h" 6 | #include 7 | 8 | namespace qqsfpm { 9 | 10 | class SwitchRoleAttached : public QObject 11 | { 12 | Q_OBJECT 13 | Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) 14 | public: 15 | SwitchRoleAttached(QObject* parent); 16 | 17 | QVariant value() const; 18 | void setValue(QVariant value); 19 | 20 | Q_SIGNALS: 21 | void valueChanged(); 22 | 23 | private: 24 | QVariant m_value; 25 | }; 26 | 27 | class SwitchRole : public SingleRole, public FilterContainer 28 | { 29 | Q_OBJECT 30 | Q_INTERFACES(qqsfpm::FilterContainer) 31 | Q_PROPERTY(QString defaultRoleName READ defaultRoleName WRITE setDefaultRoleName NOTIFY defaultRoleNameChanged) 32 | Q_PROPERTY(QVariant defaultValue READ defaultValue WRITE setDefaultValue NOTIFY defaultValueChanged) 33 | Q_PROPERTY(QQmlListProperty filters READ filtersListProperty) 34 | Q_CLASSINFO("DefaultProperty", "filters") 35 | 36 | public: 37 | using SingleRole::SingleRole; 38 | 39 | QString defaultRoleName() const; 40 | void setDefaultRoleName(const QString& defaultRoleName); 41 | 42 | QVariant defaultValue() const; 43 | void setDefaultValue(const QVariant& defaultValue); 44 | 45 | void proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) override; 46 | 47 | static SwitchRoleAttached* qmlAttachedProperties(QObject* object); 48 | 49 | Q_SIGNALS: 50 | void defaultRoleNameChanged(); 51 | void defaultValueChanged(); 52 | 53 | private: 54 | QVariant data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) override; 55 | 56 | void onFilterAppended(Filter *filter) override; 57 | void onFilterRemoved(Filter *filter) override; 58 | void onFiltersCleared() override; 59 | 60 | QString m_defaultRoleName; 61 | QVariant m_defaultValue; 62 | }; 63 | 64 | } 65 | 66 | QML_DECLARE_TYPEINFO(qqsfpm::SwitchRole, QML_HAS_ATTACHED_PROPERTIES) 67 | 68 | #endif // SWITCHROLE_H 69 | -------------------------------------------------------------------------------- /src/versioncheck/versioncheck.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Remy van Elst 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | * 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | 27 | class VersionCheck : public QObject 28 | { 29 | Q_OBJECT 30 | Q_PROPERTY(QString currentVersion READ currentVersion WRITE setCurrentVersion NOTIFY currentVersionChanged) 31 | Q_PROPERTY(bool newVersionAvailable READ newVersionAvailable NOTIFY newVersionAvailableChanged FINAL) 32 | 33 | public: 34 | explicit VersionCheck(QObject *parent = nullptr); 35 | 36 | //properties 37 | const QString ¤tVersion() const; 38 | bool newVersionAvailable() const; 39 | 40 | signals: 41 | void currentVersionChanged(const QString ¤tVersion); 42 | void newVersionAvailableChanged(); 43 | 44 | public slots: 45 | 46 | void setCurrentVersion(const QString &newCurrentVersion); 47 | void slotFinished(); 48 | void getCurrentVersion(); 49 | void setNewVersionAvailable(bool newNewVersionAvailable); 50 | 51 | private: 52 | QNetworkAccessManager* _manager; 53 | 54 | QTimer* _timer; 55 | QString _currentVersion; 56 | QUrl _url = QUrl("http://update.leafnode.nl/certinfo.txt"); 57 | void checkVersionOutput(const QString& newVersion); 58 | bool m_newVersionAvailable; 59 | 60 | static inline const QRegularExpression versionNumberFormat = QRegularExpression("[0-9]{4}\\.[0-9]{2}\\.[0-9]\\.[0-9]"); // 2022.01.0.0 61 | 62 | 63 | 64 | }; 65 | 66 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/filtercontainer.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTERCONTAINER_H 2 | #define FILTERCONTAINER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace qqsfpm { 10 | 11 | class Filter; 12 | class QQmlSortFilterProxyModel; 13 | 14 | class FilterContainer { 15 | public: 16 | virtual ~FilterContainer() = default; 17 | 18 | QList filters() const; 19 | void appendFilter(Filter* filter); 20 | void removeFilter(Filter* filter); 21 | void clearFilters(); 22 | 23 | QQmlListProperty filtersListProperty(); 24 | 25 | protected: 26 | QList m_filters; 27 | 28 | private: 29 | virtual void onFilterAppended(Filter* filter) = 0; 30 | virtual void onFilterRemoved(Filter* filter) = 0; 31 | virtual void onFiltersCleared() = 0; 32 | 33 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 34 | using sizetype = int; 35 | #else 36 | using sizetype = qsizetype; 37 | #endif 38 | 39 | static void append_filter(QQmlListProperty* list, Filter* filter); 40 | static sizetype count_filter(QQmlListProperty* list); 41 | static Filter* at_filter(QQmlListProperty* list, sizetype index); 42 | static void clear_filters(QQmlListProperty* list); 43 | }; 44 | 45 | class FilterContainerAttached : public QObject 46 | { 47 | Q_OBJECT 48 | Q_PROPERTY(QObject* container READ container WRITE setContainer NOTIFY containerChanged) 49 | 50 | public: 51 | FilterContainerAttached(QObject* object); 52 | ~FilterContainerAttached(); 53 | 54 | QObject* container() const; 55 | void setContainer(QObject* object); 56 | 57 | static FilterContainerAttached* qmlAttachedProperties(QObject* object); 58 | 59 | Q_SIGNALS: 60 | void containerChanged(); 61 | 62 | private: 63 | QPointer m_container = nullptr; 64 | Filter* m_filter = nullptr; 65 | }; 66 | 67 | } 68 | 69 | #define FilterContainer_iid "fr.grecko.SortFilterProxyModel.FilterContainer" 70 | Q_DECLARE_INTERFACE(qqsfpm::FilterContainer, FilterContainer_iid) 71 | 72 | QML_DECLARE_TYPEINFO(qqsfpm::FilterContainerAttached, QML_HAS_ATTACHED_PROPERTIES) 73 | 74 | #endif // FILTERCONTAINER_H 75 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/proxyrolecontainer.cpp: -------------------------------------------------------------------------------- 1 | #include "proxyrolecontainer.h" 2 | 3 | namespace qqsfpm { 4 | 5 | QList ProxyRoleContainer::proxyRoles() const 6 | { 7 | return m_proxyRoles; 8 | } 9 | 10 | void ProxyRoleContainer::appendProxyRole(ProxyRole* proxyRole) 11 | { 12 | m_proxyRoles.append(proxyRole); 13 | onProxyRoleAppended(proxyRole); 14 | } 15 | 16 | void ProxyRoleContainer::removeProxyRole(ProxyRole* proxyRole) 17 | { 18 | m_proxyRoles.removeOne(proxyRole); 19 | onProxyRoleRemoved(proxyRole); 20 | } 21 | 22 | void ProxyRoleContainer::clearProxyRoles() 23 | { 24 | m_proxyRoles.clear(); 25 | onProxyRolesCleared(); 26 | } 27 | 28 | QQmlListProperty ProxyRoleContainer::proxyRolesListProperty() 29 | { 30 | return QQmlListProperty(reinterpret_cast(this), &m_proxyRoles, 31 | &ProxyRoleContainer::append_proxyRole, 32 | &ProxyRoleContainer::count_proxyRole, 33 | &ProxyRoleContainer::at_proxyRole, 34 | &ProxyRoleContainer::clear_proxyRoles); 35 | } 36 | 37 | void ProxyRoleContainer::append_proxyRole(QQmlListProperty* list, ProxyRole* proxyRole) 38 | { 39 | if (!proxyRole) 40 | return; 41 | 42 | ProxyRoleContainer* that = reinterpret_cast(list->object); 43 | that->appendProxyRole(proxyRole); 44 | } 45 | 46 | qqsfpm::ProxyRoleContainer::sizetype ProxyRoleContainer::count_proxyRole(QQmlListProperty* list) 47 | { 48 | QList* ProxyRoles = static_cast*>(list->data); 49 | return ProxyRoles->count(); 50 | } 51 | 52 | ProxyRole* ProxyRoleContainer::at_proxyRole(QQmlListProperty* list, qqsfpm::ProxyRoleContainer::sizetype index) 53 | { 54 | QList* ProxyRoles = static_cast*>(list->data); 55 | return ProxyRoles->at(index); 56 | } 57 | 58 | void ProxyRoleContainer::clear_proxyRoles(QQmlListProperty *list) 59 | { 60 | ProxyRoleContainer* that = reinterpret_cast(list->object); 61 | that->clearProxyRoles(); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/sorters/sortercontainer.h: -------------------------------------------------------------------------------- 1 | #ifndef SORTERSSORTERCONTAINER_H 2 | #define SORTERSSORTERCONTAINER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace qqsfpm { 10 | 11 | class Sorter; 12 | class QQmlSortFilterProxyModel; 13 | 14 | class SorterContainer { 15 | public: 16 | virtual ~SorterContainer() = default; 17 | 18 | QList sorters() const; 19 | void appendSorter(Sorter* sorter); 20 | void removeSorter(Sorter* sorter); 21 | void clearSorters(); 22 | 23 | QQmlListProperty sortersListProperty(); 24 | 25 | protected: 26 | QList m_sorters; 27 | 28 | private: 29 | virtual void onSorterAppended(Sorter* sorter) = 0; 30 | virtual void onSorterRemoved(Sorter* sorter) = 0; 31 | virtual void onSortersCleared() = 0; 32 | 33 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 34 | using sizetype = int; 35 | #else 36 | using sizetype = qsizetype; 37 | #endif 38 | 39 | static void append_sorter(QQmlListProperty* list, Sorter* sorter); 40 | static sizetype count_sorter(QQmlListProperty* list); 41 | static Sorter* at_sorter(QQmlListProperty* list, sizetype index); 42 | static void clear_sorters(QQmlListProperty* list); 43 | }; 44 | 45 | class SorterContainerAttached : public QObject 46 | { 47 | Q_OBJECT 48 | Q_PROPERTY(QObject* container READ container WRITE setContainer NOTIFY containerChanged) 49 | 50 | public: 51 | SorterContainerAttached(QObject* object); 52 | ~SorterContainerAttached(); 53 | 54 | QObject* container() const; 55 | void setContainer(QObject* object); 56 | 57 | static SorterContainerAttached* qmlAttachedProperties(QObject* object); 58 | 59 | Q_SIGNALS: 60 | void containerChanged(); 61 | 62 | private: 63 | QPointer m_container = nullptr; 64 | Sorter* m_sorter = nullptr; 65 | }; 66 | 67 | } 68 | 69 | #define SorterContainer_iid "fr.grecko.SortFilterProxyModel.SorterContainer" 70 | Q_DECLARE_INTERFACE(qqsfpm::SorterContainer, SorterContainer_iid) 71 | 72 | QML_DECLARE_TYPEINFO(qqsfpm::SorterContainerAttached, QML_HAS_ATTACHED_PROPERTIES) 73 | 74 | #endif // SORTERSSORTERCONTAINER_H 75 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/sorters/rolesorter.cpp: -------------------------------------------------------------------------------- 1 | #include "rolesorter.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | 4 | #include "qvariantlessthan.h" 5 | 6 | 7 | 8 | 9 | namespace qqsfpm { 10 | 11 | /*! 12 | \qmltype RoleSorter 13 | \inherits Sorter 14 | \inqmlmodule SortFilterProxyModel 15 | \ingroup Sorters 16 | \brief Sorts rows based on a source model role. 17 | 18 | A RoleSorter is a simple \l Sorter that sorts rows based on a source model role. 19 | 20 | In the following example, rows with be sorted by their \c lastName role : 21 | \code 22 | SortFilterProxyModel { 23 | sourceModel: contactModel 24 | sorters: RoleSorter { roleName: "lastName" } 25 | } 26 | \endcode 27 | */ 28 | 29 | /*! 30 | \qmlproperty string RoleSorter::roleName 31 | 32 | This property holds the role name that the sorter is using to query the source model's data when sorting items. 33 | */ 34 | const QString& RoleSorter::roleName() const 35 | { 36 | return m_roleName; 37 | } 38 | 39 | void RoleSorter::setRoleName(const QString& roleName) 40 | { 41 | if (m_roleName == roleName) 42 | return; 43 | 44 | m_roleName = roleName; 45 | Q_EMIT roleNameChanged(); 46 | invalidate(); 47 | } 48 | 49 | QPair RoleSorter::sourceData(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const 50 | { 51 | QPair pair; 52 | int role = proxyModel.roleForName(m_roleName); 53 | 54 | if (role == -1) 55 | return pair; 56 | 57 | pair.first = proxyModel.sourceData(sourceLeft, role); 58 | pair.second = proxyModel.sourceData(sourceRight, role); 59 | return pair; 60 | } 61 | 62 | int RoleSorter::compare(const QModelIndex &sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const 63 | { 64 | 65 | QPair pair = sourceData(sourceLeft, sourceRight, proxyModel); 66 | QVariant leftValue = pair.first; 67 | QVariant rightValue = pair.second; 68 | if (qqsfpm::lessThan(leftValue, rightValue)) 69 | return -1; 70 | if (qqsfpm::lessThan(rightValue, leftValue)) 71 | return 1; 72 | return 0; 73 | 74 | } 75 | 76 | 77 | } -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/filter.cpp: -------------------------------------------------------------------------------- 1 | #include "filter.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype Filter 8 | \qmlabstract 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup Filters 11 | \brief Base type for the \l SortFilterProxyModel filters. 12 | 13 | The Filter type cannot be used directly in a QML file. 14 | It exists to provide a set of common properties and methods, 15 | available across all the other filter types that inherit from it. 16 | Attempting to use the Filter type directly will result in an error. 17 | */ 18 | 19 | Filter::Filter(QObject *parent) : QObject(parent) 20 | { 21 | } 22 | 23 | /*! 24 | \qmlproperty bool Filter::enabled 25 | 26 | This property holds whether the filter is enabled. 27 | A disabled filter will accept every rows unconditionally (even if it's inverted). 28 | 29 | By default, filters are enabled. 30 | */ 31 | bool Filter::enabled() const 32 | { 33 | return m_enabled; 34 | } 35 | 36 | void Filter::setEnabled(bool enabled) 37 | { 38 | if (m_enabled == enabled) 39 | return; 40 | 41 | m_enabled = enabled; 42 | Q_EMIT enabledChanged(); 43 | Q_EMIT invalidated(); 44 | } 45 | 46 | /*! 47 | \qmlproperty bool Filter::inverted 48 | 49 | This property holds whether the filter is inverted. 50 | When a filter is inverted, a row normally accepted would be rejected, and vice-versa. 51 | 52 | By default, filters are not inverted. 53 | */ 54 | bool Filter::inverted() const 55 | { 56 | return m_inverted; 57 | } 58 | 59 | void Filter::setInverted(bool inverted) 60 | { 61 | if (m_inverted == inverted) 62 | return; 63 | 64 | m_inverted = inverted; 65 | Q_EMIT invertedChanged(); 66 | invalidate(); 67 | } 68 | 69 | bool Filter::filterAcceptsRow(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 70 | { 71 | return !m_enabled || filterRow(sourceIndex, proxyModel) ^ m_inverted; 72 | } 73 | 74 | void Filter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) 75 | { 76 | Q_UNUSED(proxyModel) 77 | } 78 | 79 | void Filter::invalidate() 80 | { 81 | if (m_enabled) 82 | Q_EMIT invalidated(); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/joinrole.cpp: -------------------------------------------------------------------------------- 1 | #include "joinrole.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype JoinRole 8 | \inherits SingleRole 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup ProxyRoles 11 | \brief a role made from concatenating other roles. 12 | 13 | A JoinRole is a simple \l ProxyRole that concatenates other roles. 14 | 15 | In the following example, the \c fullName role is computed by the concatenation of the \c firstName role and the \c lastName role separated by a space : 16 | \code 17 | SortFilterProxyModel { 18 | sourceModel: contactModel 19 | proxyRoles: JoinRole { 20 | name: "fullName" 21 | roleNames: ["firstName", "lastName"] 22 | } 23 | } 24 | \endcode 25 | 26 | */ 27 | 28 | /*! 29 | \qmlproperty list JoinRole::roleNames 30 | 31 | This property holds the role names that are joined by this role. 32 | */ 33 | QStringList JoinRole::roleNames() const 34 | { 35 | return m_roleNames; 36 | } 37 | 38 | void JoinRole::setRoleNames(const QStringList& roleNames) 39 | { 40 | if (m_roleNames == roleNames) 41 | return; 42 | 43 | m_roleNames = roleNames; 44 | Q_EMIT roleNamesChanged(); 45 | invalidate(); 46 | } 47 | 48 | /*! 49 | \qmlproperty string JoinRole::separator 50 | 51 | This property holds the separator that is used to join the roles specified in \l roleNames. 52 | 53 | By default, it's a space. 54 | */ 55 | QString JoinRole::separator() const 56 | { 57 | return m_separator; 58 | } 59 | 60 | void JoinRole::setSeparator(const QString& separator) 61 | { 62 | if (m_separator == separator) 63 | return; 64 | 65 | m_separator = separator; 66 | Q_EMIT separatorChanged(); 67 | invalidate(); 68 | } 69 | 70 | QVariant JoinRole::data(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel& proxyModel) 71 | { 72 | QString result; 73 | 74 | for (const QString& roleName : m_roleNames) 75 | result += proxyModel.sourceData(sourceIndex, roleName).toString() + m_separator; 76 | 77 | if (!m_roleNames.isEmpty()) 78 | result.chop(m_separator.length()); 79 | 80 | return result; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/domainsources/domainslisttextfile.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #pragma once 18 | 19 | 20 | #include "src/listmodel/domaincountlistmodel.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | typedef QPair QIntPair; 27 | 28 | 29 | class DomainsListTextFile : public QObject 30 | { 31 | Q_OBJECT 32 | Q_PROPERTY(domainCountListModel* domains READ domains NOTIFY domainsChanged FINAL) 33 | Q_PROPERTY(QStringList hostnames READ hostnames WRITE setHostnames NOTIFY hostnamesChanged FINAL) 34 | Q_PROPERTY(QString textFileName READ textFileName WRITE setTextFileName NOTIFY textFileNameChanged FINAL) 35 | Q_PROPERTY(QString lastError READ lastError WRITE setLastError NOTIFY lastErrorChanged FINAL) 36 | 37 | public: 38 | explicit DomainsListTextFile(QObject *parent = nullptr); 39 | 40 | Q_INVOKABLE void getHostnamesFromTextFile(const QUrl path); 41 | 42 | QStringList hostnames() const; 43 | 44 | QString textFileName() const; 45 | void setTextFileName(const QString &newTextFileName); 46 | 47 | domainCountListModel *domains() const; 48 | 49 | QString lastError() const; 50 | void setLastError(const QString &newLastError); 51 | 52 | void setHostnames(const QStringList &newHostnames); 53 | 54 | signals: 55 | void hostnamesChanged(); 56 | void textFileNameChanged(); 57 | 58 | void domainsChanged(); 59 | 60 | void lastErrorChanged(); 61 | 62 | private: 63 | QStringList m_hostnames; 64 | QString m_textFileName; 65 | domainCountListModel *m_domains = nullptr; 66 | QString m_lastError; 67 | }; 68 | 69 | -------------------------------------------------------------------------------- /CertInfo.pro: -------------------------------------------------------------------------------- 1 | QT += quick sql widgets quickcontrols2 2 | 3 | # You can make your code fail to compile if it uses deprecated APIs. 4 | # In order to do so, uncomment the following line. 5 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 6 | 7 | QMAKE_CXXFLAGS = -Wno-deprecated-declarations 8 | 9 | VERSION = 2024.01.2.0 # major.minor.patch.build 10 | DEFINES += APP_VERSION=\\\"$$VERSION\\\" 11 | QMAKE_TARGET_PRODUCT = "CertInfo" 12 | QMAKE_TARGET_COMPANY = "Sparkling Network" 13 | QMAKE_TARGET_DESCRIPTION = "Which root certificates should you trust? by Remy van Elst, https://raymii.org" 14 | QMAKE_TARGET_COPYRIGHT = "Remy van Elst - Licensed under the GNU GPLv3" 15 | 16 | RC_ICONS = certinfo.ico 17 | 18 | HEADERS += \ 19 | src/ca/caconcurrentgatherer.h \ 20 | src/ca/certificate.h \ 21 | src/domainsources/browserhistorydb.h \ 22 | src/listmodel/caissuerlistmodel.h \ 23 | src/ca/caprocessor.h \ 24 | src/listmodel/domaincountlistmodel.h \ 25 | src/domainsources/domainslisttextfile.h \ 26 | src/listmodel/genericlistmodel.h \ 27 | src/listmodel/qabstractlistmodelwithrowcountsignal.h \ 28 | src/versioncheck/versioncheck.h 29 | 30 | SOURCES += \ 31 | src/ca/caconcurrentgatherer.cpp \ 32 | src/domainsources/browserhistorydb.cpp \ 33 | src/listmodel/caissuerlistmodel.cpp \ 34 | src/ca/caprocessor.cpp \ 35 | src/listmodel/domaincountlistmodel.cpp \ 36 | src/domainsources/domainslisttextfile.cpp \ 37 | src/main.cpp \ 38 | src/versioncheck/versioncheck.cpp 39 | 40 | CONFIG += c++17 41 | 42 | RESOURCES += src/qml.qrc 43 | 44 | contains(QT_MAJOR_VERSION, 6) { 45 | RESOURCES += src/qt6.qrc 46 | } 47 | 48 | contains(QT_MAJOR_VERSION, 5) { 49 | RESOURCES += src/qt5.qrc 50 | } 51 | 52 | # Additional import path used to resolve QML modules in Qt Creator's code model 53 | QML_IMPORT_PATH = 54 | 55 | # Additional import path used to resolve QML modules just for Qt Quick Designer 56 | QML_DESIGNER_IMPORT_PATH = 57 | 58 | # Default rules for deployment. 59 | qnx: target.path = /tmp/$${TARGET}/bin 60 | else: unix:!android: target.path = /opt/$${TARGET}/bin 61 | !isEmpty(target.path): INSTALLS += target 62 | 63 | 64 | include(src/thirdparty/SortFilterProxyModel/SortFilterProxyModel.pri) 65 | 66 | DISTFILES += \ 67 | README.md \ 68 | certinfo.ico 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/sorters/filtersorter.cpp: -------------------------------------------------------------------------------- 1 | #include "filtersorter.h" 2 | #include "filters/filter.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype FilterSorter 8 | \inherits Sorter 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup Sorters 11 | \ingroup FilterContainer 12 | \brief Sorts rows based on if they match filters. 13 | 14 | A FilterSorter is a \l Sorter that orders row matching its filters before the rows not matching the filters. 15 | 16 | In the following example, rows with their \c favorite role set to \c true will be ordered at the beginning : 17 | \code 18 | SortFilterProxyModel { 19 | sourceModel: contactModel 20 | sorters: FilterSorter { 21 | ValueFilter { roleName: "favorite"; value: true } 22 | } 23 | } 24 | \endcode 25 | \sa FilterContainer 26 | */ 27 | 28 | /*! 29 | \qmlproperty list FilterSorter::filters 30 | \default 31 | 32 | This property holds the list of filters for this filter sorter. 33 | If a row match all this FilterSorter's filters, it will be ordered before rows not matching all the filters. 34 | 35 | \sa Filter, FilterContainer 36 | */ 37 | 38 | int FilterSorter::compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel &proxyModel) const 39 | { 40 | bool leftIsAccepted = indexIsAccepted(sourceLeft, proxyModel); 41 | bool rightIsAccepted = indexIsAccepted(sourceRight, proxyModel); 42 | 43 | if (leftIsAccepted == rightIsAccepted) 44 | return 0; 45 | 46 | return leftIsAccepted ? -1 : 1; 47 | } 48 | 49 | void FilterSorter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) 50 | { 51 | for (Filter* filter : m_filters) 52 | filter->proxyModelCompleted(proxyModel); 53 | } 54 | 55 | void FilterSorter::onFilterAppended(Filter* filter) 56 | { 57 | connect(filter, &Filter::invalidated, this, &FilterSorter::invalidate); 58 | invalidate(); 59 | } 60 | 61 | void FilterSorter::onFilterRemoved(Filter* filter) 62 | { 63 | disconnect(filter, &Filter::invalidated, this, &FilterSorter::invalidate); 64 | invalidate(); 65 | } 66 | 67 | void FilterSorter::onFiltersCleared() 68 | { 69 | invalidate(); 70 | } 71 | 72 | bool FilterSorter::indexIsAccepted(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 73 | { 74 | return std::all_of(m_filters.begin(), m_filters.end(), 75 | [&] (Filter* filter) { 76 | return filter->filterAcceptsRow(sourceIndex, proxyModel); 77 | } 78 | ); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/SortFilterProxyModel.pri: -------------------------------------------------------------------------------- 1 | !contains( CONFIG, c\+\+1[147] ): warning("SortFilterProxyModel needs at least c++11, add CONFIG += c++11 to your .pro") 2 | 3 | INCLUDEPATH += $$PWD 4 | 5 | HEADERS += $$PWD/qqmlsortfilterproxymodel.h \ 6 | $$PWD/qvariantlessthan.h \ 7 | $$PWD/filters/filter.h \ 8 | $$PWD/filters/filtercontainer.h \ 9 | $$PWD/filters/rolefilter.h \ 10 | $$PWD/filters/valuefilter.h \ 11 | $$PWD/filters/indexfilter.h \ 12 | $$PWD/filters/regexpfilter.h \ 13 | $$PWD/filters/rangefilter.h \ 14 | $$PWD/filters/expressionfilter.h \ 15 | $$PWD/filters/filtercontainerfilter.h \ 16 | $$PWD/filters/anyoffilter.h \ 17 | $$PWD/filters/alloffilter.h \ 18 | $$PWD/sorters/sorter.h \ 19 | $$PWD/sorters/sortercontainer.h \ 20 | $$PWD/sorters/rolesorter.h \ 21 | $$PWD/sorters/stringsorter.h \ 22 | $$PWD/sorters/expressionsorter.h \ 23 | $$PWD/proxyroles/proxyrole.h \ 24 | $$PWD/proxyroles/proxyrolecontainer.h \ 25 | $$PWD/proxyroles/joinrole.h \ 26 | $$PWD/proxyroles/switchrole.h \ 27 | $$PWD/proxyroles/expressionrole.h \ 28 | $$PWD/proxyroles/singlerole.h \ 29 | $$PWD/proxyroles/regexprole.h \ 30 | $$PWD/sorters/filtersorter.h \ 31 | $$PWD/proxyroles/filterrole.h \ 32 | $$PWD/utils/utils.h 33 | 34 | SOURCES += $$PWD/qqmlsortfilterproxymodel.cpp \ 35 | $$PWD/qvariantlessthan.cpp \ 36 | $$PWD/filters/filter.cpp \ 37 | $$PWD/filters/filtercontainer.cpp \ 38 | $$PWD/filters/rolefilter.cpp \ 39 | $$PWD/filters/valuefilter.cpp \ 40 | $$PWD/filters/indexfilter.cpp \ 41 | $$PWD/filters/regexpfilter.cpp \ 42 | $$PWD/filters/rangefilter.cpp \ 43 | $$PWD/filters/expressionfilter.cpp \ 44 | $$PWD/filters/filtercontainerfilter.cpp \ 45 | $$PWD/filters/anyoffilter.cpp \ 46 | $$PWD/filters/alloffilter.cpp \ 47 | $$PWD/filters/filtersqmltypes.cpp \ 48 | $$PWD/sorters/sorter.cpp \ 49 | $$PWD/sorters/sortercontainer.cpp \ 50 | $$PWD/sorters/rolesorter.cpp \ 51 | $$PWD/sorters/stringsorter.cpp \ 52 | $$PWD/sorters/expressionsorter.cpp \ 53 | $$PWD/sorters/sortersqmltypes.cpp \ 54 | $$PWD/proxyroles/proxyrole.cpp \ 55 | $$PWD/proxyroles/proxyrolecontainer.cpp \ 56 | $$PWD/proxyroles/joinrole.cpp \ 57 | $$PWD/proxyroles/switchrole.cpp \ 58 | $$PWD/proxyroles/expressionrole.cpp \ 59 | $$PWD/proxyroles/proxyrolesqmltypes.cpp \ 60 | $$PWD/proxyroles/singlerole.cpp \ 61 | $$PWD/proxyroles/regexprole.cpp \ 62 | $$PWD/sorters/filtersorter.cpp \ 63 | $$PWD/proxyroles/filterrole.cpp \ 64 | $$PWD/utils/utils.cpp 65 | -------------------------------------------------------------------------------- /src/domainsources/browserhistorydb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "src/listmodel/domaincountlistmodel.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | typedef QPair QIntPair; 27 | 28 | class BrowserHistoryDb : public QObject 29 | { 30 | Q_OBJECT 31 | Q_PROPERTY(domainCountListModel* domains READ domains NOTIFY domainsChanged FINAL) 32 | Q_PROPERTY(QStringList hostnames READ hostnames WRITE setHostnames NOTIFY hostnamesChanged FINAL) 33 | Q_PROPERTY(QString dbFileName READ dbFileName WRITE setDbFileName NOTIFY dbFileNameChanged FINAL) 34 | Q_PROPERTY(bool isFirefox READ isFirefox WRITE setIsFirefox NOTIFY isFirefoxChanged FINAL) 35 | Q_PROPERTY(QString lastDbError READ lastDbError WRITE setLastDbError NOTIFY lastDbErrorChanged FINAL) 36 | 37 | public: 38 | explicit BrowserHistoryDb(QObject *parent = nullptr); 39 | 40 | Q_INVOKABLE bool openDb(const QUrl path); 41 | Q_INVOKABLE void getHostnamesFromDb(); 42 | 43 | QStringList hostnames() const; 44 | void setHostnames(const QStringList &newHostnames); 45 | 46 | QString dbFileName() const; 47 | void setDbFileName(const QString &newDbFileName); 48 | 49 | QString lastDbError() const; 50 | void setLastDbError(const QString &newLastDbError); 51 | 52 | domainCountListModel *domains() const; 53 | 54 | bool isFirefox() const; 55 | void setIsFirefox(bool newIsFirefox); 56 | 57 | signals: 58 | 59 | void hostnamesChanged(); 60 | void dbFileNameChanged(); 61 | void lastDbErrorChanged(); 62 | void domainsChanged(); 63 | void isFirefoxChanged(); 64 | 65 | private: 66 | QSqlDatabase _db; 67 | QString firefoxQuery(); 68 | QString chromeQuery(); 69 | QStringList m_hostnames; 70 | QString m_dbFileName; 71 | QString m_lastDbError; 72 | domainCountListModel *m_domains = nullptr; 73 | bool m_isFirefox; 74 | }; 75 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/regexpfilter.cpp: -------------------------------------------------------------------------------- 1 | #include "regexpfilter.h" 2 | #include 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype RegExpFilter 8 | \inherits RoleFilter 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup Filters 11 | \brief Filters rows matching a regular expression. 12 | 13 | A RegExpFilter is a \l RoleFilter that accepts rows matching a regular rexpression. 14 | 15 | In the following example, only rows with their \c lastName role beggining with the content of textfield the will be accepted: 16 | \code 17 | TextField { 18 | id: nameTextField 19 | } 20 | 21 | SortFilterProxyModel { 22 | sourceModel: contactModel 23 | filters: RegExpFilter { 24 | roleName: "lastName" 25 | pattern: "^" + nameTextField.displayText 26 | } 27 | } 28 | \endcode 29 | */ 30 | 31 | /*! 32 | \qmlproperty bool RegExpFilter::pattern 33 | 34 | The pattern used to filter the contents of the source model. 35 | 36 | \sa syntax 37 | */ 38 | RegExpFilter::RegExpFilter() : 39 | m_caseSensitivity(m_regExp.patternOptions().testFlag( 40 | QRegularExpression::CaseInsensitiveOption) ? Qt::CaseInsensitive : Qt::CaseSensitive) 41 | { 42 | } 43 | 44 | QString RegExpFilter::pattern() const 45 | { 46 | return m_pattern; 47 | } 48 | 49 | void RegExpFilter::setPattern(const QString& pattern) 50 | { 51 | if (m_pattern == pattern) 52 | return; 53 | 54 | m_pattern = pattern; 55 | m_regExp.setPattern(pattern); 56 | Q_EMIT patternChanged(); 57 | invalidate(); 58 | } 59 | 60 | /*! 61 | \qmlproperty Qt::CaseSensitivity RegExpFilter::caseSensitivity 62 | 63 | This property holds the caseSensitivity of the filter. 64 | */ 65 | Qt::CaseSensitivity RegExpFilter::caseSensitivity() const 66 | { 67 | return m_caseSensitivity; 68 | } 69 | 70 | void RegExpFilter::setCaseSensitivity(Qt::CaseSensitivity caseSensitivity) 71 | { 72 | if (m_caseSensitivity == caseSensitivity) 73 | return; 74 | 75 | m_caseSensitivity = caseSensitivity; 76 | QRegularExpression::PatternOptions patternOptions = m_regExp.patternOptions(); 77 | if (caseSensitivity == Qt::CaseInsensitive) 78 | patternOptions.setFlag(QRegularExpression::CaseInsensitiveOption); 79 | m_regExp.setPatternOptions(patternOptions); 80 | Q_EMIT caseSensitivityChanged(); 81 | invalidate(); 82 | } 83 | 84 | bool RegExpFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 85 | { 86 | const QString string = sourceData(sourceIndex, proxyModel).toString(); 87 | return m_regExp.match(string).hasMatch(); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/FoldableCertInfo.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | 18 | import QtQuick 2.15 19 | import QtQuick.Controls 2.15 20 | 21 | Column { 22 | id: root 23 | default property alias children: content.children 24 | property var modelData 25 | property string title: modelData.subject 26 | property color titleBgColor: "#e0e0e0" 27 | property color titleBgColorPressed: "#cfcfcf" 28 | property color titleBorderColor: titleBgColor 29 | property bool collapsed: true 30 | 31 | Button { 32 | id: header 33 | anchors.left: parent.left 34 | width: parent.width 35 | clip: true 36 | height: 48 37 | contentItem: Text { 38 | height: parent.height 39 | anchors.margins: 5 40 | width: parent.width 41 | text: root.title 42 | horizontalAlignment : Text.AlignLeft 43 | minimumPixelSize: 12 44 | fontSizeMode: Text.Fit 45 | wrapMode: Text.WordWrap 46 | elide: Text.ElideMiddle 47 | } 48 | 49 | onClicked: root.collapsed = !root.collapsed 50 | background: Rectangle { 51 | color: parent.down ? titleBgColorPressed : titleBgColor 52 | border.color: titleBorderColor 53 | border.width: 1 54 | radius: 2 55 | } 56 | } 57 | 58 | Pane { 59 | id: contentPane 60 | 61 | visible: height > 0 62 | height: !root.collapsed ? implicitHeight : 0 63 | 64 | Behavior on height { 65 | NumberAnimation { 66 | easing.type: Easing.InOutQuad 67 | duration: 200 68 | } 69 | } 70 | 71 | clip: true 72 | anchors.left: parent.left 73 | anchors.right: parent.right 74 | 75 | Column { 76 | id: content 77 | anchors.right: parent.right 78 | anchors.left: parent.left 79 | spacing: 10 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/listmodel/caissuerlistmodel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | 18 | #include "caissuerlistmodel.h" 19 | 20 | CACertificateListModel::CACertificateListModel(QObject* parent) : GenericListModel(parent) 21 | { 22 | addSelector("subject", [](const Certificate &i) { return i.subject; }); 23 | addSelector("subjectInfo", [](const Certificate &i) { return QVariant::fromValue(i.subjectInfo); }); 24 | addSelector("string", [](const Certificate &i) { return QString::fromStdString(i.toString()); }); 25 | addSelector("issuer", [](const Certificate &i) { return i.issuer; }); 26 | addSelector("issuerInfo", [](const Certificate &i) { return QVariant::fromValue(i.issuerInfo); }); 27 | addSelector("validFromDate", [](const Certificate &i) { return i.validFromDate.toString(); }); 28 | addSelector("validUntilDate", [](const Certificate &i) { return i.validUntilDate.toString(); }); 29 | addSelector("count", [](const Certificate &i) { return i.count; }); 30 | addSelector("isca", [](const Certificate &i) { return i.isCA; }); 31 | addSelector("istrustedrootca", [](const Certificate &i) { return i.isSystemTrustedRootCA; }); 32 | addSelector("domains", [](const Certificate &i) { return i.domains.join(" "); }); 33 | addSelector("subjectAlternativeNames", [](const Certificate &i) { return i.subjectAlternativeNames.join(" "); }); 34 | addSelector("isselfsigned", [](const Certificate &i) { return i.isSelfSigned; }); 35 | addSelector("errors", [](const Certificate &i) { return i.errors.join(" "); }); 36 | } 37 | 38 | void CACertificateListModel::addOrUpdateItem(const Certificate &item, const QString& findBySubject) 39 | { 40 | auto it = std::find_if(m_listObjects.begin(), m_listObjects.end(), [&findBySubject](const Certificate& c){ return c.subject == findBySubject;}); 41 | if(it != m_listObjects.end()) 42 | { 43 | int index = std::distance(m_listObjects.begin(), it); 44 | updateRow(index, item); 45 | } else { 46 | addRow(item); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /setup.iss: -------------------------------------------------------------------------------- 1 | ; Script generated by the Inno Setup Script Wizard. 2 | ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! 3 | 4 | ; build deploy on windows 5 | ; \qt5\5.15.2\mingw81_32\bin\qtenv2.bat 6 | ; cd \Users\user\git\build-CertInfo-Desktop_Qt_5_15_2_MinGW_32_bit-Release\release 7 | ; \qt5\5.15.2\mingw81_32\bin\windeployqt.exe CertInfo.exe --qmldir \Users\user\git\CertInfo\src\ 8 | 9 | #define MyAppName "CertInfo" 10 | #define MyAppVersion "2024.01" 11 | #define MyAppPublisher "Sparkling Network" 12 | #define MyAppURL "https://raymii.org/" 13 | #define MyAppExeName "CertInfo.exe" 14 | 15 | [Setup] 16 | ; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications. 17 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) 18 | AppId={{67E9D8A9-58D4-4989-B54D-09E39CA26C65}} 19 | AppName={#MyAppName} 20 | AppVersion={#MyAppVersion} 21 | ;AppVerName={#MyAppName} {#MyAppVersion} 22 | AppPublisher={#MyAppPublisher} 23 | AppPublisherURL={#MyAppURL} 24 | AppSupportURL={#MyAppURL} 25 | AppUpdatesURL={#MyAppURL} 26 | DefaultDirName={autopf}\{#MyAppName} 27 | DefaultGroupName={#MyAppName} 28 | LicenseFile=C:\Users\user\git\CertInfo\LICENSE 29 | ; Uncomment the following line to run in non administrative install mode (install for current user only.) 30 | ;PrivilegesRequired=lowest 31 | PrivilegesRequiredOverridesAllowed=dialog 32 | OutputDir=C:\Users\user\git\setup 33 | OutputBaseFilename=CertInfoSetup 34 | SetupIconFile=C:\Users\user\git\CertInfo\certinfo.ico 35 | Compression=lzma 36 | SolidCompression=yes 37 | WizardStyle=modern 38 | 39 | [Languages] 40 | Name: "english"; MessagesFile: "compiler:Default.isl" 41 | 42 | [Tasks] 43 | Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked 44 | 45 | [Files] 46 | Source: "C:\Users\user\git\build-CertInfo-Desktop_Qt_5_15_2_MinGW_32_bit-Release\release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion 47 | Source: "C:\Users\user\git\build-CertInfo-Desktop_Qt_5_15_2_MinGW_32_bit-Release\release\*"; DestDir: "{app}"; Excludes: "*.cpp,*.o,moc*"; Flags: ignoreversion recursesubdirs createallsubdirs 48 | Source: "C:\Qt5\Tools\OpenSSL\Win_x86\bin\*.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs 49 | Source: "C:\windows\system32\msvcr100.dll"; DestDir: "{app}"; 50 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files 51 | 52 | [Icons] 53 | Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" 54 | Name: "{group}\{cm:ProgramOnTheWeb,{#MyAppName}}"; Filename: "{#MyAppURL}" 55 | Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}" 56 | Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon 57 | 58 | [Run] 59 | Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent 60 | 61 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/utils/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include 3 | #include 4 | 5 | namespace qqsfpm { 6 | 7 | int compareVariants(const QVariant &lhs, const QVariant &rhs) 8 | { 9 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // qt 5 10 | // Do the QString check first because otherwise the canConvert check will get hit for strings. 11 | if (static_cast(lhs.type()) == QMetaType::QString && static_cast(rhs.type()) == QMetaType::QString) { 12 | const auto lhsValue = lhs.toString(); 13 | const auto rhsValue = rhs.toString(); 14 | if (lhsValue == rhsValue) 15 | return 0; 16 | return lhsValue.compare(rhsValue, Qt::CaseInsensitive); 17 | } else if (static_cast(lhs.type()) == QMetaType::Bool && static_cast(rhs.type()) == QMetaType::Bool) { 18 | const auto lhsValue = lhs.toBool(); 19 | const auto rhsValue = rhs.toBool(); 20 | if (lhsValue == rhsValue) 21 | return 0; 22 | // false < true. 23 | return !lhsValue ? -1 : 1; 24 | } else if (static_cast(lhs.type()) == QMetaType::QDate && static_cast(rhs.type()) == QMetaType::QDate) { 25 | const auto lhsValue = lhs.toDate(); 26 | const auto rhsValue = rhs.toDate(); 27 | if (lhsValue == rhsValue) 28 | return 0; 29 | return lhsValue < rhsValue ? -1 : 1; 30 | } else if (static_cast(lhs.type()) == QMetaType::QDateTime && static_cast(rhs.type()) == QMetaType::QDateTime) { 31 | const auto lhsValue = lhs.toDateTime(); 32 | const auto rhsValue = rhs.toDateTime(); 33 | if (lhsValue == rhsValue) 34 | return 0; 35 | return lhsValue < rhsValue ? -1 : 1; 36 | } else if (static_cast(lhs.type()) == QMetaType::QStringList && static_cast(rhs.type()) == QMetaType::QStringList) { 37 | const auto lhsValue = lhs.toStringList(); 38 | const auto rhsValue = rhs.toStringList(); 39 | if (lhsValue == rhsValue) 40 | return 0; 41 | return lhsValue < rhsValue ? -1 : 1; 42 | } else if (lhs.canConvert() && rhs.canConvert()) { 43 | const auto lhsValue = lhs.toInt(); 44 | const auto rhsValue = rhs.toInt(); 45 | if (lhsValue == rhsValue) 46 | return 0; 47 | return lhsValue < rhsValue ? -1 : 1; 48 | } else if (lhs.canConvert() && rhs.canConvert()) { 49 | const auto lhsValue = lhs.toReal(); 50 | const auto rhsValue = rhs.toReal(); 51 | if (qFuzzyCompare(lhsValue, rhsValue)) 52 | return 0; 53 | return lhsValue < rhsValue ? -1 : 1; 54 | } 55 | 56 | qWarning() << "Don't know how to compare" << lhs << "against" << rhs << "- returning 0"; 57 | return 0; 58 | #else 59 | return QPartialOrdering::Less == QVariant::compare(lhs, rhs); 60 | #endif 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/qvariantlessthan.cpp: -------------------------------------------------------------------------------- 1 | #include "qvariantlessthan.h" 2 | 3 | #include 4 | 5 | namespace qqsfpm { 6 | 7 | /*! 8 | \brief Less-than operator for generic QVariants 9 | 10 | Since Qt 5.15 deprecated the less-than operator of QVariant, we 11 | have to provide our own implementation. On older Qt versions, 12 | use the original implementation. 13 | 14 | Includes special implementations for numberic types, char, date and 15 | time. Everything else is converted to String and compared then. 16 | */ 17 | bool lessThan(const QVariant &lhs, const QVariant &rhs) 18 | { 19 | #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) 20 | return lhs < rhs; 21 | #else 22 | static const auto numericTypes = QVector{ 23 | QMetaType::Int, 24 | QMetaType::UInt, 25 | QMetaType::LongLong, 26 | QMetaType::ULongLong, 27 | QMetaType::Float, 28 | QMetaType::Double, 29 | }; 30 | static const auto unsignedTypes = QVector{ 31 | QMetaType::UInt, 32 | QMetaType::ULongLong, 33 | }; 34 | static const auto dateTimeTypes = QVector{ 35 | QMetaType::QDate, 36 | QMetaType::QTime, 37 | QMetaType::QDateTime, 38 | }; 39 | 40 | const auto lt = static_cast(lhs.userType()); 41 | const auto rt = static_cast(rhs.userType()); 42 | if (numericTypes.contains(lt) && numericTypes.contains(rt)) { 43 | if (lt == QMetaType::Double || lt == QMetaType::Float 44 | || rt == QMetaType::Double || rt == QMetaType::Float) { 45 | return lhs.toDouble() < rhs.toDouble(); 46 | } else { 47 | const auto ul = unsignedTypes.contains(lt); 48 | const auto ur = unsignedTypes.contains(rt); 49 | if (ul && ur) { 50 | return lhs.toULongLong() < rhs.toULongLong(); 51 | } else if (!ul && !ur) { 52 | return lhs.toLongLong() < rhs.toLongLong(); 53 | } else if (ul) { 54 | const auto r = rhs.toLongLong(); 55 | return r > 0 && 56 | lhs.toULongLong() < static_cast(r); 57 | } else { 58 | const auto l = lhs.toLongLong(); 59 | return l < 0 || 60 | static_cast(l) < rhs.toULongLong(); 61 | } 62 | } 63 | } else if (dateTimeTypes.contains(lt) && dateTimeTypes.contains(rt)) { 64 | if (lt == QMetaType::QDate && rt == QMetaType::QDate) { 65 | return lhs.toDate() < rhs.toDate(); 66 | } else if (lt == QMetaType::QTime && rt == QMetaType::QTime) { 67 | return lhs.toTime() < rhs.toTime(); 68 | } else { 69 | return lhs.toDateTime() < rhs.toDateTime(); 70 | } 71 | } else if (lt == QMetaType::Char && rt == QMetaType::Char) { 72 | return lhs.toChar() < rhs.toChar(); 73 | } else { 74 | return lhs.toString() < rhs.toString(); 75 | } 76 | #endif 77 | } 78 | 79 | } // namespace qqsfpm 80 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | 18 | #include "src/ca/caconcurrentgatherer.h" 19 | #include "src/domainsources/browserhistorydb.h" 20 | #include "src/domainsources/domainslisttextfile.h" 21 | #include "src/versioncheck/versioncheck.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | void destroy(QObject*); 32 | 33 | 34 | int main(int argc, char *argv[]) 35 | { 36 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 37 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 38 | #endif 39 | QGuiApplication app(argc, argv); 40 | 41 | app.setOrganizationName("Sparkling Network"); 42 | app.setOrganizationDomain("raymii.org"); 43 | app.setApplicationName("FF Cert Cleanup"); 44 | app.setWindowIcon(QIcon(":/certinfo.png")); 45 | app.setApplicationVersion(APP_VERSION); 46 | 47 | qmlRegisterType("org.raymii.BrowserHistoryDB", 1, 0, "BrowserHistoryDB"); 48 | qmlRegisterType("org.raymii.DomainsListTextFile", 1, 0, "DomainsListTextFile"); 49 | qmlRegisterType("org.raymii.CAConcurrentGatherer", 1, 0, "CAConcurrentGatherer"); 50 | qmlRegisterType("org.raymii.VersionCheck", 1, 0, "VersionCheck"); 51 | 52 | QStringList selectors; 53 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 54 | selectors << "qt5"; 55 | #else 56 | selectors << "qt6"; 57 | QQuickStyle::setStyle("Material"); 58 | #endif 59 | 60 | 61 | 62 | 63 | QQmlApplicationEngine* engine = new QQmlApplicationEngine; 64 | QQmlFileSelector* selector = new QQmlFileSelector(engine); 65 | selector->setExtraSelectors(selectors); 66 | 67 | const QUrl url(QStringLiteral("qrc:/main.qml")); 68 | QObject::connect(engine, &QQmlApplicationEngine::objectCreated, 69 | &app, [url](QObject *obj, const QUrl &objUrl) { 70 | if (!obj && url == objUrl) 71 | QCoreApplication::exit(-1); 72 | }, Qt::QueuedConnection); 73 | engine->load(url); 74 | 75 | int appExecResult = app.exec(); 76 | 77 | destroy(engine); 78 | 79 | return appExecResult; 80 | } 81 | 82 | void destroy(QObject* object) 83 | { 84 | QEventLoop eventloop; 85 | QObject::connect(object, &QObject::destroyed, &eventloop, &QEventLoop::quit); 86 | object->deleteLater(); 87 | eventloop.exec(); 88 | } 89 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/indexfilter.cpp: -------------------------------------------------------------------------------- 1 | #include "indexfilter.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype IndexFilter 8 | \inherits Filter 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup Filters 11 | \brief Filters rows based on their source index. 12 | 13 | An IndexFilter is a filter allowing contents to be filtered based on their source model index. 14 | 15 | In the following example, only the first row of the source model will be accepted: 16 | \code 17 | SortFilterProxyModel { 18 | sourceModel: contactModel 19 | filters: IndexFilter { 20 | maximumIndex: 0 21 | } 22 | } 23 | \endcode 24 | */ 25 | 26 | /*! 27 | \qmlproperty int IndexFilter::minimumIndex 28 | 29 | This property holds the minimumIndex of the filter. 30 | Rows with a source index lower than \c minimumIndex will be rejected. 31 | 32 | If \c minimumIndex is negative, it is counted from the end of the source model, meaning that : 33 | \code 34 | minimumIndex: -1 35 | \endcode 36 | is equivalent to : 37 | \code 38 | minimumIndex: sourceModel.count - 1 39 | \endcode 40 | By default, no value is set. 41 | */ 42 | const QVariant& IndexFilter::minimumIndex() const 43 | { 44 | return m_minimumIndex; 45 | } 46 | 47 | void IndexFilter::setMinimumIndex(const QVariant& minimumIndex) 48 | { 49 | if (m_minimumIndex == minimumIndex) 50 | return; 51 | 52 | m_minimumIndex = minimumIndex; 53 | Q_EMIT minimumIndexChanged(); 54 | invalidate(); 55 | } 56 | 57 | /*! 58 | \qmlproperty int IndexFilter::maximumIndex 59 | 60 | This property holds the maximumIndex of the filter. 61 | Rows with a source index higher than \c maximumIndex will be rejected. 62 | 63 | If \c maximumIndex is negative, it is counted from the end of the source model, meaning that: 64 | \code 65 | maximumIndex: -1 66 | \endcode 67 | is equivalent to : 68 | \code 69 | maximumIndex: sourceModel.count - 1 70 | \endcode 71 | By default, no value is set. 72 | */ 73 | const QVariant& IndexFilter::maximumIndex() const 74 | { 75 | return m_maximumIndex; 76 | } 77 | 78 | void IndexFilter::setMaximumIndex(const QVariant& maximumIndex) 79 | { 80 | if (m_maximumIndex == maximumIndex) 81 | return; 82 | 83 | m_maximumIndex = maximumIndex; 84 | Q_EMIT maximumIndexChanged(); 85 | invalidate(); 86 | } 87 | 88 | bool IndexFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 89 | { 90 | int sourceRowCount = proxyModel.sourceModel()->rowCount(); 91 | int sourceRow = sourceIndex.row(); 92 | 93 | bool minimumIsValid; 94 | int minimum = m_minimumIndex.toInt(&minimumIsValid); 95 | if (minimumIsValid) { 96 | int actualMinimum = minimum < 0 ? sourceRowCount + minimum : minimum; 97 | if (sourceRow < actualMinimum) 98 | return false; 99 | } 100 | 101 | bool maximumIsValid; 102 | int maximum = m_maximumIndex.toInt(&maximumIsValid); 103 | if (maximumIsValid) { 104 | int actualMaximum = maximum < 0 ? sourceRowCount + maximum : maximum; 105 | if (sourceRow > actualMaximum) 106 | return false; 107 | } 108 | 109 | return true; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/domainsources/domainslisttextfile.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | 18 | #include "domainslisttextfile.h" 19 | #include 20 | #include 21 | #include 22 | 23 | DomainsListTextFile::DomainsListTextFile(QObject *parent) 24 | : QObject{parent} 25 | { 26 | m_domains = new domainCountListModel(this); 27 | } 28 | 29 | void DomainsListTextFile::getHostnamesFromTextFile(const QUrl path) 30 | { 31 | setTextFileName(path.toLocalFile()); 32 | 33 | QStringList hostnames; 34 | QMap countsHash; 35 | QList countsList; 36 | 37 | QFile file(textFileName()); 38 | if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { 39 | setLastError(file.errorString()); 40 | } 41 | 42 | QTextStream in(&file); 43 | while (!in.atEnd()) { 44 | QString line = in.readLine(); 45 | if(!line.isEmpty()) { 46 | countsHash[line]++; 47 | } 48 | } 49 | 50 | file.close(); 51 | 52 | QMap::iterator i; 53 | for(i = countsHash.begin(); i != countsHash.end(); ++i) { 54 | countsList.push_back({i.key(), i.value()}); 55 | } 56 | 57 | std::sort(countsList.begin(), countsList.end(), [](const QIntPair& a, const QIntPair& b) { return a.second > b.second; }); 58 | 59 | for(i = countsHash.begin(); i != countsHash.end(); ++i) { 60 | hostnames.push_back(i.key()); 61 | } 62 | setHostnames(hostnames); 63 | m_domains->updateFromQList(countsList); 64 | } 65 | 66 | QStringList DomainsListTextFile::hostnames() const 67 | { 68 | return m_hostnames; 69 | } 70 | 71 | QString DomainsListTextFile::textFileName() const 72 | { 73 | return m_textFileName; 74 | } 75 | 76 | void DomainsListTextFile::setTextFileName(const QString &newTextFileName) 77 | { 78 | if (m_textFileName == newTextFileName) 79 | return; 80 | m_textFileName = newTextFileName; 81 | emit textFileNameChanged(); 82 | } 83 | 84 | domainCountListModel *DomainsListTextFile::domains() const 85 | { 86 | return m_domains; 87 | } 88 | 89 | QString DomainsListTextFile::lastError() const 90 | { 91 | return m_lastError; 92 | } 93 | 94 | void DomainsListTextFile::setLastError(const QString &newLastError) 95 | { 96 | if (m_lastError == newLastError) 97 | return; 98 | m_lastError = newLastError; 99 | emit lastErrorChanged(); 100 | } 101 | 102 | void DomainsListTextFile::setHostnames(const QStringList &newHostnames) 103 | { 104 | if (m_hostnames == newHostnames) 105 | return; 106 | m_hostnames = newHostnames; 107 | emit hostnamesChanged(); 108 | } 109 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/regexprole.cpp: -------------------------------------------------------------------------------- 1 | #include "regexprole.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | #include 4 | 5 | namespace qqsfpm { 6 | 7 | /*! 8 | \qmltype RegExpRole 9 | \inherits ProxyRole 10 | \inqmlmodule SortFilterProxyModel 11 | \ingroup ProxyRoles 12 | \brief A ProxyRole extracting data from a source role via a regular expression. 13 | 14 | A RegExpRole is a \l ProxyRole that provides a role for each named capture group of its regular expression \l pattern. 15 | 16 | In the following example, the \c date role of the source model will be extracted in 3 roles in the proxy moodel: \c year, \c month and \c day. 17 | \code 18 | SortFilterProxyModel { 19 | sourceModel: eventModel 20 | proxyRoles: RegExpRole { 21 | roleName: "date" 22 | pattern: "(?\\d{4})-(?\\d{2})-(?\\d{2})" 23 | } 24 | } 25 | \endcode 26 | */ 27 | 28 | /*! 29 | \qmlproperty QString RegExpRole::roleName 30 | 31 | This property holds the role name that the RegExpRole is using to query the source model's data to extract new roles from. 32 | */ 33 | QString RegExpRole::roleName() const 34 | { 35 | return m_roleName; 36 | } 37 | 38 | void RegExpRole::setRoleName(const QString& roleName) 39 | { 40 | if (m_roleName == roleName) 41 | return; 42 | 43 | m_roleName = roleName; 44 | Q_EMIT roleNameChanged(); 45 | } 46 | 47 | /*! 48 | \qmlproperty QString RegExpRole::pattern 49 | 50 | This property holds the pattern of the regular expression of this RegExpRole. 51 | The RegExpRole will expose a role for each of the named capture group of the pattern. 52 | */ 53 | QString RegExpRole::pattern() const 54 | { 55 | return m_regularExpression.pattern(); 56 | } 57 | 58 | void RegExpRole::setPattern(const QString& pattern) 59 | { 60 | if (m_regularExpression.pattern() == pattern) 61 | return; 62 | 63 | Q_EMIT namesAboutToBeChanged(); 64 | m_regularExpression.setPattern(pattern); 65 | invalidate(); 66 | Q_EMIT patternChanged(); 67 | Q_EMIT namesChanged(); 68 | } 69 | 70 | /*! 71 | \qmlproperty Qt::CaseSensitivity RegExpRole::caseSensitivity 72 | 73 | This property holds the caseSensitivity of the regular expression. 74 | */ 75 | Qt::CaseSensitivity RegExpRole::caseSensitivity() const 76 | { 77 | return m_regularExpression.patternOptions() & QRegularExpression::CaseInsensitiveOption ? 78 | Qt::CaseInsensitive : Qt::CaseSensitive; 79 | } 80 | 81 | void RegExpRole::setCaseSensitivity(Qt::CaseSensitivity caseSensitivity) 82 | { 83 | if (this->caseSensitivity() == caseSensitivity) 84 | return; 85 | 86 | m_regularExpression.setPatternOptions(m_regularExpression.patternOptions() ^ QRegularExpression::CaseInsensitiveOption); //toggle the option 87 | Q_EMIT caseSensitivityChanged(); 88 | } 89 | 90 | QStringList RegExpRole::names() 91 | { 92 | QStringList nameCaptureGroups = m_regularExpression.namedCaptureGroups(); 93 | nameCaptureGroups.removeAll(""); 94 | return nameCaptureGroups; 95 | } 96 | 97 | QVariant RegExpRole::data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel, const QString &name) 98 | { 99 | QString text = proxyModel.sourceData(sourceIndex, m_roleName).toString(); 100 | QRegularExpressionMatch match = m_regularExpression.match(text); 101 | return match.hasMatch() ? (match.captured(name)) : QVariant{}; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/sorters/stringsorter.cpp: -------------------------------------------------------------------------------- 1 | #include "stringsorter.h" 2 | 3 | namespace qqsfpm { 4 | 5 | /*! 6 | \qmltype StringSorter 7 | \inherits RoleSorter 8 | \inqmlmodule SortFilterProxyModel 9 | \ingroup Sorters 10 | \brief Sorts rows based on a source model string role. 11 | 12 | \l StringSorter is a specialized \l RoleSorter that sorts rows based on a source model string role. 13 | \l StringSorter compares strings according to a localized collation algorithm. 14 | 15 | In the following example, rows with be sorted by their \c lastName role : 16 | \code 17 | SortFilterProxyModel { 18 | sourceModel: contactModel 19 | sorters: StringSorter { roleName: "lastName" } 20 | } 21 | \endcode 22 | */ 23 | 24 | /*! 25 | \qmlproperty Qt.CaseSensitivity StringSorter::caseSensitivity 26 | 27 | This property holds the case sensitivity of the sorter. 28 | */ 29 | Qt::CaseSensitivity StringSorter::caseSensitivity() const 30 | { 31 | return m_collator.caseSensitivity(); 32 | } 33 | 34 | void StringSorter::setCaseSensitivity(Qt::CaseSensitivity caseSensitivity) 35 | { 36 | if (m_collator.caseSensitivity() == caseSensitivity) 37 | return; 38 | 39 | m_collator.setCaseSensitivity(caseSensitivity); 40 | Q_EMIT caseSensitivityChanged(); 41 | invalidate(); 42 | } 43 | 44 | /*! 45 | \qmlproperty bool StringSorter::ignorePunctation 46 | 47 | This property holds whether the sorter ignores punctation. 48 | if \c ignorePunctuation is \c true, punctuation characters and symbols are ignored when determining sort order. 49 | 50 | \note This property is not currently supported on Apple platforms or if Qt is configured to not use ICU on Linux. 51 | */ 52 | bool StringSorter::ignorePunctation() const 53 | { 54 | return m_collator.ignorePunctuation(); 55 | } 56 | 57 | void StringSorter::setIgnorePunctation(bool ignorePunctation) 58 | { 59 | if (m_collator.ignorePunctuation() == ignorePunctation) 60 | return; 61 | 62 | m_collator.setIgnorePunctuation(ignorePunctation); 63 | Q_EMIT ignorePunctationChanged(); 64 | invalidate(); 65 | } 66 | 67 | /*! 68 | \qmlproperty Locale StringSorter::locale 69 | 70 | This property holds the locale of the sorter. 71 | */ 72 | QLocale StringSorter::locale() const 73 | { 74 | return m_collator.locale(); 75 | } 76 | 77 | void StringSorter::setLocale(const QLocale &locale) 78 | { 79 | if (m_collator.locale() == locale) 80 | return; 81 | 82 | m_collator.setLocale(locale); 83 | Q_EMIT localeChanged(); 84 | invalidate(); 85 | } 86 | 87 | /*! 88 | \qmlproperty bool StringSorter::numericMode 89 | 90 | This property holds whether the numeric mode of the sorter is enabled. 91 | This will enable proper sorting of numeric digits, so that e.g. 100 sorts after 99. 92 | By default this mode is off. 93 | */ 94 | bool StringSorter::numericMode() const 95 | { 96 | return m_collator.numericMode(); 97 | } 98 | 99 | void StringSorter::setNumericMode(bool numericMode) 100 | { 101 | if (m_collator.numericMode() == numericMode) 102 | return; 103 | 104 | m_collator.setNumericMode(numericMode); 105 | Q_EMIT numericModeChanged(); 106 | invalidate(); 107 | } 108 | 109 | int StringSorter::compare(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const QQmlSortFilterProxyModel& proxyModel) const 110 | { 111 | QPair pair = sourceData(sourceLeft, sourceRight, proxyModel); 112 | QString leftValue = pair.first.toString(); 113 | QString rightValue = pair.second.toString(); 114 | return m_collator.compare(leftValue, rightValue); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/ca/caconcurrentgatherer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "src/listmodel/caissuerlistmodel.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | typedef QPair QIntPair; 29 | 30 | class CAConcurrentGatherer : public QObject 31 | { 32 | Q_OBJECT 33 | Q_PROPERTY(bool stop READ stop WRITE setStop NOTIFY stopChanged FINAL) 34 | Q_PROPERTY(QStringList hostnames READ hostnames WRITE setHostnames NOTIFY hostnamesChanged FINAL) 35 | Q_PROPERTY(CACertificateListModel* issuersCounted READ issuersCounted NOTIFY issuersCountedChanged FINAL) 36 | Q_PROPERTY(CACertificateListModel* notInUseSystemRootCAs READ notInUseSystemRootCAs NOTIFY notInUseSystemRootCAsChanged FINAL) 37 | Q_PROPERTY(bool busy READ busy WRITE setBusy NOTIFY busyChanged FINAL) 38 | Q_PROPERTY(QString statusText READ statusText WRITE setStatusText NOTIFY statusTextChanged FINAL) 39 | Q_PROPERTY(int progress READ progress WRITE setProgress NOTIFY progressChanged FINAL) 40 | Q_PROPERTY(int privateProgress READ privateProgress WRITE setPrivateProgress NOTIFY privateProgressChanged FINAL) 41 | 42 | public: 43 | explicit CAConcurrentGatherer(QObject *parent = nullptr); 44 | 45 | Q_INVOKABLE void clear(); 46 | Q_INVOKABLE void startGatherCertificatesInBackground(); 47 | Q_INVOKABLE void exportToText(const QUrl& path); 48 | 49 | QStringList hostnames() const; 50 | void setHostnames(const QStringList &newHostnames); 51 | 52 | CACertificateListModel *issuersCounted() const; 53 | 54 | CACertificateListModel *notInUseSystemRootCAs() const; 55 | 56 | bool busy() const; 57 | void setBusy(bool newBusy); 58 | 59 | QString statusText() const; 60 | void setStatusText(const QString &newStatusText); 61 | 62 | int progress() const; 63 | void setProgress(int newProgress); 64 | 65 | int privateProgress() const; 66 | void setPrivateProgress(int newPrivateProgress); 67 | 68 | bool stop() const; 69 | void setStop(bool newStop); 70 | 71 | signals: 72 | void hostnamesChanged(); 73 | void issuersCountedChanged(); 74 | void threadBucketFinished(); 75 | void allThreadsFinished(); 76 | void busyChanged(); 77 | void statusTextChanged(); 78 | void progressChanged(); 79 | void privateProgressChanged(int newProgress); 80 | void notInUseSystemRootCAsChanged(); 81 | void stopChanged(); 82 | 83 | private slots: 84 | void onThreadBucketFinished(); 85 | void onAllThreadsFinished(); 86 | 87 | private: 88 | bool _busy = false; 89 | QStringList m_hostnames; 90 | void gatherCertificates(); 91 | void checkNonInUseSystemRootCAs(); 92 | QList _systemCerts; 93 | QList _notInUseSystemRootCAList; 94 | QHash resultHash; 95 | QList resultList; 96 | CACertificateListModel *m_issuersCounted = nullptr; 97 | CACertificateListModel *_notInUseSystemRootCAs = nullptr; 98 | QString m_statusText; 99 | int m_progress; 100 | int m_privateProgress; 101 | std::atomic m_stop = false; 102 | }; 103 | 104 | Q_DECLARE_METATYPE(QIntPair) 105 | -------------------------------------------------------------------------------- /src/versioncheck/versioncheck.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Remy van Elst 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | * 16 | */ 17 | 18 | #include "versioncheck.h" 19 | #include 20 | #include 21 | #include 22 | 23 | VersionCheck::VersionCheck(QObject *parent) : QObject(parent) 24 | { 25 | _manager = new QNetworkAccessManager(this); 26 | _timer = new QTimer(this); 27 | 28 | int updateCheckInterval = QRandomGenerator::global()->bounded(80000, 300000); 29 | 30 | _timer->setInterval(updateCheckInterval); 31 | QObject::connect(_timer, &QTimer::timeout, this, &VersionCheck::getCurrentVersion); 32 | _timer->start(); 33 | 34 | QObject::connect(this, &VersionCheck::currentVersionChanged, this, &VersionCheck::checkVersionOutput); 35 | 36 | getCurrentVersion(); 37 | } 38 | 39 | void VersionCheck::getCurrentVersion() 40 | { 41 | auto _request = QScopedPointer(new QNetworkRequest()); 42 | _request->setUrl(_url); 43 | _request->setTransferTimeout(5000); 44 | QString ua = "CertInfo v" + QString(APP_VERSION) + " " + QGuiApplication::platformName() + "; "; 45 | 46 | _request->setRawHeader("User-Agent", ua.toUtf8()); 47 | _request->setRawHeader("Version", QString(APP_VERSION).toUtf8()); 48 | 49 | QNetworkReply *reply = _manager->get(*_request); 50 | QObject::connect(reply, &QNetworkReply::finished, this, &VersionCheck::slotFinished); 51 | } 52 | 53 | const QString &VersionCheck::currentVersion() const 54 | { 55 | return _currentVersion; 56 | } 57 | 58 | bool VersionCheck::newVersionAvailable() const 59 | { 60 | return m_newVersionAvailable; 61 | } 62 | 63 | void VersionCheck::setCurrentVersion(const QString &newCurrentVersion) 64 | { 65 | if (_currentVersion == newCurrentVersion) 66 | return; 67 | _currentVersion = newCurrentVersion; 68 | emit currentVersionChanged(_currentVersion); 69 | } 70 | 71 | void VersionCheck::slotFinished() 72 | { 73 | QNetworkReply *reply = dynamic_cast(sender()); 74 | if(reply != nullptr) { 75 | QString output = reply->readAll(); 76 | if(output.contains(versionNumberFormat)) { 77 | setCurrentVersion(output); 78 | } 79 | reply->deleteLater(); 80 | } 81 | } 82 | 83 | 84 | void VersionCheck::checkVersionOutput(const QString &newVersion) 85 | { 86 | bool updateAvailable = false; 87 | 88 | QString thisVersionString = APP_VERSION; 89 | thisVersionString.remove(QChar('.'), Qt::CaseInsensitive); 90 | 91 | QString newVersionString = newVersion; 92 | newVersionString.remove(QChar('.'), Qt::CaseInsensitive); 93 | 94 | bool thisVersionToLLSuccess = false; 95 | unsigned long long thisVersionNumber = thisVersionString.toULongLong(&thisVersionToLLSuccess); 96 | 97 | bool newVersionToLLSuccess = false; 98 | unsigned long long newVersionNumber = newVersionString.toULongLong(&newVersionToLLSuccess); 99 | 100 | if(newVersionToLLSuccess && thisVersionToLLSuccess) 101 | { 102 | if(newVersionNumber > thisVersionNumber) { 103 | updateAvailable = true; 104 | } 105 | } 106 | 107 | setNewVersionAvailable(updateAvailable); 108 | } 109 | 110 | void VersionCheck::setNewVersionAvailable(bool newNewVersionAvailable) 111 | { 112 | if (m_newVersionAvailable == newNewVersionAvailable) 113 | return; 114 | m_newVersionAvailable = newNewVersionAvailable; 115 | emit newVersionAvailableChanged(); 116 | } 117 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/sorters/sorter.cpp: -------------------------------------------------------------------------------- 1 | #include "sorter.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | 4 | namespace qqsfpm { 5 | 6 | /*! 7 | \qmltype Sorter 8 | \qmlabstract 9 | \inqmlmodule SortFilterProxyModel 10 | \ingroup Sorters 11 | \brief Base type for the \l SortFilterProxyModel sorters. 12 | 13 | The Sorter type cannot be used directly in a QML file. 14 | It exists to provide a set of common properties and methods, 15 | available across all the other sorters types that inherit from it. 16 | Attempting to use the Sorter type directly will result in an error. 17 | */ 18 | 19 | Sorter::Sorter(QObject *parent) : QObject(parent) 20 | { 21 | } 22 | 23 | Sorter::~Sorter() = default; 24 | 25 | /*! 26 | \qmlproperty bool Sorter::enabled 27 | 28 | This property holds whether the sorter is enabled. 29 | A disabled sorter will not change the order of the rows. 30 | 31 | By default, sorters are enabled. 32 | */ 33 | bool Sorter::enabled() const 34 | { 35 | return m_enabled; 36 | } 37 | 38 | void Sorter::setEnabled(bool enabled) 39 | { 40 | if (m_enabled == enabled) 41 | return; 42 | 43 | m_enabled = enabled; 44 | Q_EMIT enabledChanged(); 45 | Q_EMIT invalidated(); 46 | } 47 | 48 | bool Sorter::ascendingOrder() const 49 | { 50 | return sortOrder() == Qt::AscendingOrder; 51 | } 52 | 53 | void Sorter::setAscendingOrder(bool ascendingOrder) 54 | { 55 | setSortOrder(ascendingOrder ? Qt::AscendingOrder : Qt::DescendingOrder); 56 | } 57 | 58 | 59 | /*! 60 | \qmlproperty Qt::SortOrder Sorter::sortOrder 61 | 62 | This property holds the sort order of this sorter. 63 | 64 | \value Qt.AscendingOrder The items are sorted ascending e.g. starts with 'AAA' ends with 'ZZZ' in Latin-1 locales 65 | \value Qt.DescendingOrder The items are sorted descending e.g. starts with 'ZZZ' ends with 'AAA' in Latin-1 locales 66 | 67 | By default, sorting is in ascending order. 68 | */ 69 | Qt::SortOrder Sorter::sortOrder() const 70 | { 71 | return m_sortOrder; 72 | } 73 | 74 | void Sorter::setSortOrder(Qt::SortOrder sortOrder) 75 | { 76 | if (m_sortOrder == sortOrder) 77 | return; 78 | 79 | m_sortOrder = sortOrder; 80 | Q_EMIT sortOrderChanged(); 81 | invalidate(); 82 | } 83 | 84 | /*! 85 | \qmlproperty int Sorter::priority 86 | 87 | This property holds the sort priority of this sorter. 88 | Sorters with a higher priority are applied first. 89 | In case of equal priority, Sorters are ordered by their insertion order. 90 | 91 | By default, the priority is 0. 92 | */ 93 | int Sorter::priority() const 94 | { 95 | return m_priority; 96 | } 97 | 98 | void Sorter::setPriority(int priority) 99 | { 100 | if (m_priority == priority) 101 | return; 102 | 103 | m_priority = priority; 104 | Q_EMIT priorityChanged(); 105 | invalidate(); 106 | } 107 | 108 | int Sorter::compareRows(const QModelIndex &source_left, const QModelIndex &source_right, const QQmlSortFilterProxyModel& proxyModel) const 109 | { 110 | int comparison = compare(source_left, source_right, proxyModel); 111 | return (m_sortOrder == Qt::AscendingOrder) ? comparison : -comparison; 112 | } 113 | 114 | int Sorter::compare(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const QQmlSortFilterProxyModel& proxyModel) const 115 | { 116 | if (lessThan(sourceLeft, sourceRight, proxyModel)) 117 | return -1; 118 | if (lessThan(sourceRight, sourceLeft, proxyModel)) 119 | return 1; 120 | return 0; 121 | } 122 | 123 | void Sorter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) 124 | { 125 | Q_UNUSED(proxyModel) 126 | } 127 | 128 | bool Sorter::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight, const QQmlSortFilterProxyModel& proxyModel) const 129 | { 130 | Q_UNUSED(sourceLeft) 131 | Q_UNUSED(sourceRight) 132 | Q_UNUSED(proxyModel) 133 | return false; 134 | } 135 | 136 | void Sorter::invalidate() 137 | { 138 | if (m_enabled) 139 | Q_EMIT invalidated(); 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/filtercontainer.cpp: -------------------------------------------------------------------------------- 1 | #include "filtercontainer.h" 2 | #include "filter.h" 3 | #include 4 | 5 | namespace qqsfpm { 6 | 7 | /*! 8 | \qmltype FilterContainer 9 | \qmlabstract 10 | \inqmlmodule SortFilterProxyModel 11 | \ingroup FilterAttached 12 | \brief Abstract interface for types containing \l {Filter}{Filters}. 13 | 14 | \section2 Types implementing this interface: 15 | \annotatedlist FilterContainer 16 | */ 17 | 18 | QList FilterContainer::filters() const 19 | { 20 | return m_filters; 21 | } 22 | 23 | void FilterContainer::appendFilter(Filter* filter) 24 | { 25 | m_filters.append(filter); 26 | onFilterAppended(filter); 27 | } 28 | 29 | void FilterContainer::removeFilter(Filter* filter) 30 | { 31 | m_filters.removeOne(filter); 32 | onFilterRemoved(filter); 33 | } 34 | 35 | void FilterContainer::clearFilters() 36 | { 37 | m_filters.clear(); 38 | onFiltersCleared(); 39 | } 40 | 41 | QQmlListProperty FilterContainer::filtersListProperty() 42 | { 43 | return QQmlListProperty(reinterpret_cast(this), &m_filters, 44 | &FilterContainer::append_filter, 45 | &FilterContainer::count_filter, 46 | &FilterContainer::at_filter, 47 | &FilterContainer::clear_filters); 48 | } 49 | 50 | void FilterContainer::append_filter(QQmlListProperty* list, Filter* filter) 51 | { 52 | if (!filter) 53 | return; 54 | 55 | FilterContainer* that = reinterpret_cast(list->object); 56 | that->appendFilter(filter); 57 | } 58 | 59 | qqsfpm::FilterContainer::sizetype FilterContainer::count_filter(QQmlListProperty* list) 60 | { 61 | QList* filters = static_cast*>(list->data); 62 | return filters->count(); 63 | } 64 | 65 | Filter* FilterContainer::at_filter(QQmlListProperty* list, qqsfpm::FilterContainer::sizetype index) 66 | { 67 | QList* filters = static_cast*>(list->data); 68 | return filters->at(index); 69 | } 70 | 71 | void FilterContainer::clear_filters(QQmlListProperty *list) 72 | { 73 | FilterContainer* that = reinterpret_cast(list->object); 74 | that->clearFilters(); 75 | } 76 | 77 | FilterContainerAttached::FilterContainerAttached(QObject* object) : QObject(object), 78 | m_filter(qobject_cast(object)) 79 | { 80 | if (!m_filter) 81 | qmlWarning(object) << "FilterContainer must be attached to a Filter"; 82 | } 83 | 84 | FilterContainerAttached::~FilterContainerAttached() 85 | { 86 | if (m_filter && m_container) { 87 | FilterContainer* container = qobject_cast(m_container.data()); 88 | container->removeFilter(m_filter); 89 | } 90 | } 91 | 92 | /*! 93 | \qmlattachedproperty bool FilterContainer::container 94 | This attached property allows you to include in a \l FilterContainer a \l Filter that 95 | has been instantiated outside of the \l FilterContainer, for example in an Instantiator. 96 | */ 97 | QObject* FilterContainerAttached::container() const 98 | { 99 | return m_container; 100 | } 101 | 102 | void FilterContainerAttached::setContainer(QObject* object) 103 | { 104 | if (m_container == object) 105 | return; 106 | 107 | FilterContainer* container = qobject_cast(object); 108 | if (object && !container) 109 | qmlWarning(parent()) << "container must inherits from FilterContainer, " << object->metaObject()->className() << " provided"; 110 | 111 | if (m_container && m_filter) 112 | qobject_cast(m_container.data())->removeFilter(m_filter); 113 | 114 | m_container = container ? object : nullptr; 115 | if (container && m_filter) 116 | container->appendFilter(m_filter); 117 | 118 | Q_EMIT containerChanged(); 119 | } 120 | 121 | FilterContainerAttached* FilterContainerAttached::qmlAttachedProperties(QObject* object) 122 | { 123 | return new FilterContainerAttached(object); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/sorters/sortercontainer.cpp: -------------------------------------------------------------------------------- 1 | #include "sortercontainer.h" 2 | #include "sorter.h" 3 | #include 4 | 5 | namespace qqsfpm { 6 | 7 | /*! 8 | \qmltype SorterContainer 9 | \qmlabstract 10 | \inqmlmodule SortFilterProxyModel 11 | \ingroup SorterAttached 12 | \brief Abstract interface for types containing \l {Sorter}{Sorters}. 13 | 14 | \section2 Types implementing this interface: 15 | \annotatedlist SorterContainer 16 | */ 17 | 18 | QList SorterContainer::sorters() const 19 | { 20 | return m_sorters; 21 | } 22 | 23 | void SorterContainer::appendSorter(Sorter* sorter) 24 | { 25 | m_sorters.append(sorter); 26 | onSorterAppended(sorter); 27 | } 28 | 29 | void SorterContainer::removeSorter(Sorter *sorter) 30 | { 31 | m_sorters.removeOne(sorter); 32 | onSorterRemoved(sorter); 33 | } 34 | 35 | void SorterContainer::clearSorters() 36 | { 37 | m_sorters.clear(); 38 | onSortersCleared(); 39 | } 40 | 41 | QQmlListProperty SorterContainer::sortersListProperty() 42 | { 43 | return QQmlListProperty(reinterpret_cast(this), &m_sorters, 44 | &SorterContainer::append_sorter, 45 | &SorterContainer::count_sorter, 46 | &SorterContainer::at_sorter, 47 | &SorterContainer::clear_sorters); 48 | } 49 | 50 | void SorterContainer::append_sorter(QQmlListProperty* list, Sorter* sorter) 51 | { 52 | if (!sorter) 53 | return; 54 | 55 | SorterContainer* that = reinterpret_cast(list->object); 56 | that->appendSorter(sorter); 57 | } 58 | 59 | SorterContainer::sizetype SorterContainer::count_sorter(QQmlListProperty* list) 60 | { 61 | QList* sorters = static_cast*>(list->data); 62 | return sorters->count(); 63 | } 64 | 65 | Sorter* SorterContainer::at_sorter(QQmlListProperty* list, qqsfpm::SorterContainer::sizetype index) 66 | { 67 | QList* sorters = static_cast*>(list->data); 68 | return sorters->at(index); 69 | } 70 | 71 | void SorterContainer::clear_sorters(QQmlListProperty *list) 72 | { 73 | SorterContainer* that = reinterpret_cast(list->object); 74 | that->clearSorters(); 75 | } 76 | 77 | SorterContainerAttached::SorterContainerAttached(QObject* object) : QObject(object), 78 | m_sorter(qobject_cast(object)) 79 | { 80 | if (!m_sorter) 81 | qmlWarning(object) << "SorterContainerAttached must be attached to a Sorter"; 82 | } 83 | 84 | SorterContainerAttached::~SorterContainerAttached() 85 | { 86 | if (m_sorter && m_container) { 87 | SorterContainer* container = qobject_cast(m_container.data()); 88 | container->removeSorter(m_sorter); 89 | } 90 | } 91 | 92 | /*! 93 | \qmlattachedproperty bool SorterContainer::container 94 | This attached property allows you to include in a \l SorterContainer a \l Sorter that 95 | has been instantiated outside of the \l SorterContainer, for example in an Instantiator. 96 | */ 97 | QObject* SorterContainerAttached::container() const 98 | { 99 | return m_container; 100 | } 101 | 102 | void SorterContainerAttached::setContainer(QObject* object) 103 | { 104 | if (m_container == object) 105 | return; 106 | 107 | SorterContainer* container = qobject_cast(object); 108 | if (object && !container) 109 | qmlWarning(parent()) << "container must inherits from SorterContainer, " << object->metaObject()->className() << " provided"; 110 | 111 | if (m_container && m_sorter) 112 | qobject_cast(m_container.data())->removeSorter(m_sorter); 113 | 114 | m_container = container ? object : nullptr; 115 | if (container && m_sorter) 116 | container->appendSorter(m_sorter); 117 | 118 | Q_EMIT containerChanged(); 119 | } 120 | 121 | SorterContainerAttached* SorterContainerAttached::qmlAttachedProperties(QObject* object) 122 | { 123 | return new SorterContainerAttached(object); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/rangefilter.cpp: -------------------------------------------------------------------------------- 1 | #include "rangefilter.h" 2 | 3 | #include "qvariantlessthan.h" 4 | 5 | 6 | 7 | 8 | namespace qqsfpm { 9 | 10 | /*! 11 | \qmltype RangeFilter 12 | \inherits RoleFilter 13 | \inqmlmodule SortFilterProxyModel 14 | \ingroup Filters 15 | \brief Filters rows between boundary values. 16 | 17 | A RangeFilter is a \l RoleFilter that accepts rows if their data is between the filter's minimum and maximum value. 18 | 19 | In the following example, only rows with their \c price role set to a value between the tow boundary of the slider will be accepted : 20 | \code 21 | RangeSlider { 22 | id: priceRangeSlider 23 | } 24 | 25 | SortFilterProxyModel { 26 | sourceModel: priceModel 27 | filters: RangeFilter { 28 | roleName: "price" 29 | minimumValue: priceRangeSlider.first.value 30 | maximumValue: priceRangeSlider.second.value 31 | } 32 | } 33 | \endcode 34 | */ 35 | 36 | /*! 37 | \qmlproperty int RangeFilter::minimumValue 38 | 39 | This property holds the minimumValue of the filter. 40 | Rows with a value lower than \c minimumValue will be rejected. 41 | 42 | By default, no value is set. 43 | 44 | \sa minimumInclusive 45 | */ 46 | QVariant RangeFilter::minimumValue() const 47 | { 48 | return m_minimumValue; 49 | } 50 | 51 | void RangeFilter::setMinimumValue(QVariant minimumValue) 52 | { 53 | if (m_minimumValue == minimumValue) 54 | return; 55 | 56 | m_minimumValue = minimumValue; 57 | Q_EMIT minimumValueChanged(); 58 | invalidate(); 59 | } 60 | 61 | /*! 62 | \qmlproperty int RangeFilter::minimumInclusive 63 | 64 | This property holds whether the \l minimumValue is inclusive. 65 | 66 | By default, the \l minimumValue is inclusive. 67 | 68 | \sa minimumValue 69 | */ 70 | bool RangeFilter::minimumInclusive() const 71 | { 72 | return m_minimumInclusive; 73 | } 74 | 75 | void RangeFilter::setMinimumInclusive(bool minimumInclusive) 76 | { 77 | if (m_minimumInclusive == minimumInclusive) 78 | return; 79 | 80 | m_minimumInclusive = minimumInclusive; 81 | Q_EMIT minimumInclusiveChanged(); 82 | invalidate(); 83 | } 84 | 85 | /*! 86 | \qmlproperty int RangeFilter::maximumValue 87 | 88 | This property holds the maximumValue of the filter. 89 | Rows with a value higher than \c maximumValue will be rejected. 90 | 91 | By default, no value is set. 92 | 93 | \sa maximumInclusive 94 | */ 95 | QVariant RangeFilter::maximumValue() const 96 | { 97 | return m_maximumValue; 98 | } 99 | 100 | void RangeFilter::setMaximumValue(QVariant maximumValue) 101 | { 102 | if (m_maximumValue == maximumValue) 103 | return; 104 | 105 | m_maximumValue = maximumValue; 106 | Q_EMIT maximumValueChanged(); 107 | invalidate(); 108 | } 109 | 110 | /*! 111 | \qmlproperty int RangeFilter::maximumInclusive 112 | 113 | This property holds whether the \l minimumValue is inclusive. 114 | 115 | By default, the \l minimumValue is inclusive. 116 | 117 | \sa minimumValue 118 | */ 119 | bool RangeFilter::maximumInclusive() const 120 | { 121 | return m_maximumInclusive; 122 | } 123 | 124 | void RangeFilter::setMaximumInclusive(bool maximumInclusive) 125 | { 126 | if (m_maximumInclusive == maximumInclusive) 127 | return; 128 | 129 | m_maximumInclusive = maximumInclusive; 130 | Q_EMIT maximumInclusiveChanged(); 131 | invalidate(); 132 | } 133 | 134 | bool RangeFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 135 | { 136 | const QVariant value = sourceData(sourceIndex, proxyModel); 137 | bool lessThanMin = m_minimumValue.isValid() && 138 | (m_minimumInclusive ? qqsfpm::lessThan(value, m_minimumValue) 139 | : !qqsfpm::lessThan(m_minimumValue, value)); 140 | bool moreThanMax = m_maximumValue.isValid() && 141 | (m_maximumInclusive ? qqsfpm::lessThan(m_maximumValue, value) 142 | : !qqsfpm::lessThan(value, m_maximumValue)); 143 | return !(lessThanMin || moreThanMax); 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/expressionrole.cpp: -------------------------------------------------------------------------------- 1 | #include "expressionrole.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | #include 4 | 5 | namespace qqsfpm { 6 | 7 | /*! 8 | \qmltype ExpressionRole 9 | \inherits SingleRole 10 | \inqmlmodule SortFilterProxyModel 11 | \ingroup ProxyRoles 12 | \brief A custom role computed from a javascript expression. 13 | 14 | An ExpressionRole is a \l ProxyRole allowing to implement a custom role based on a javascript expression. 15 | 16 | In the following example, the \c c role is computed by adding the \c a role and \c b role of the model : 17 | \code 18 | SortFilterProxyModel { 19 | sourceModel: numberModel 20 | proxyRoles: ExpressionRole { 21 | name: "c" 22 | expression: model.a + model.b 23 | } 24 | } 25 | \endcode 26 | */ 27 | 28 | /*! 29 | \qmlproperty expression ExpressionRole::expression 30 | 31 | An expression to implement a custom role. 32 | It has the same syntax has a \l {http://doc.qt.io/qt-5/qtqml-syntax-propertybinding.html} {Property Binding} except it will be evaluated for each of the source model's rows. 33 | The data for this role will be the retuned valued of the expression. 34 | Data for each row is exposed like for a delegate of a QML View. 35 | 36 | This expression is reevaluated for a row every time its model data changes. 37 | When an external property (not \c index or in \c model) the expression depends on changes, the expression is reevaluated for every row of the source model. 38 | To capture the properties the expression depends on, the expression is first executed with invalid data and each property access is detected by the QML engine. 39 | This means that if a property is not accessed because of a conditional, it won't be captured and the expression won't be reevaluted when this property changes. 40 | 41 | A workaround to this problem is to access all the properties the expressions depends unconditionally at the beggining of the expression. 42 | */ 43 | const QQmlScriptString& ExpressionRole::expression() const 44 | { 45 | return m_scriptString; 46 | } 47 | 48 | void ExpressionRole::setExpression(const QQmlScriptString& scriptString) 49 | { 50 | if (m_scriptString == scriptString) 51 | return; 52 | 53 | m_scriptString = scriptString; 54 | updateExpression(); 55 | 56 | Q_EMIT expressionChanged(); 57 | invalidate(); 58 | } 59 | 60 | void ExpressionRole::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) 61 | { 62 | updateContext(proxyModel); 63 | } 64 | 65 | QVariant ExpressionRole::data(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) 66 | { 67 | if (!m_scriptString.isEmpty()) { 68 | QVariantMap modelMap; 69 | QHash roles = proxyModel.roleNames(); 70 | 71 | QQmlContext context(qmlContext(this)); 72 | auto addToContext = [&] (const QString &name, const QVariant& value) { 73 | context.setContextProperty(name, value); 74 | modelMap.insert(name, value); 75 | }; 76 | 77 | for (auto it = roles.cbegin(); it != roles.cend(); ++it) 78 | addToContext(it.value(), proxyModel.sourceData(sourceIndex, it.key())); 79 | addToContext("index", sourceIndex.row()); 80 | 81 | context.setContextProperty("model", modelMap); 82 | 83 | QQmlExpression expression(m_scriptString, &context); 84 | QVariant result = expression.evaluate(); 85 | 86 | if (expression.hasError()) { 87 | qWarning() << expression.error(); 88 | return true; 89 | } 90 | return result; 91 | } 92 | return QVariant(); 93 | } 94 | 95 | void ExpressionRole::updateContext(const QQmlSortFilterProxyModel& proxyModel) 96 | { 97 | delete m_context; 98 | m_context = new QQmlContext(qmlContext(this), this); 99 | // what about roles changes ? 100 | QVariantMap modelMap; 101 | 102 | auto addToContext = [&] (const QString &name, const QVariant& value) { 103 | m_context->setContextProperty(name, value); 104 | modelMap.insert(name, value); 105 | }; 106 | 107 | for (const QByteArray& roleName : proxyModel.roleNames().values()) 108 | addToContext(roleName, QVariant()); 109 | 110 | addToContext("index", -1); 111 | 112 | m_context->setContextProperty("model", modelMap); 113 | updateExpression(); 114 | } 115 | 116 | void ExpressionRole::updateExpression() 117 | { 118 | if (!m_context) 119 | return; 120 | 121 | delete m_expression; 122 | m_expression = new QQmlExpression(m_scriptString, m_context, 0, this); 123 | connect(m_expression, &QQmlExpression::valueChanged, this, &ExpressionRole::invalidate); 124 | m_expression->setNotifyOnValueChanged(true); 125 | m_expression->evaluate(); 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/filters/expressionfilter.cpp: -------------------------------------------------------------------------------- 1 | #include "expressionfilter.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | #include 4 | 5 | namespace qqsfpm { 6 | 7 | /*! 8 | \qmltype ExpressionFilter 9 | \inherits Filter 10 | \inqmlmodule SortFilterProxyModel 11 | \ingroup Filters 12 | \brief Filters row with a custom filtering. 13 | 14 | An ExpressionFilter is a \l Filter allowing to implement custom filtering based on a javascript expression. 15 | */ 16 | 17 | /*! 18 | \qmlproperty expression ExpressionFilter::expression 19 | 20 | An expression to implement custom filtering, it must evaluate to a boolean. 21 | It has the same syntax has a \l {http://doc.qt.io/qt-5/qtqml-syntax-propertybinding.html} {Property Binding} except it will be evaluated for each of the source model's rows. 22 | Rows that have their expression evaluating to \c true will be accepted by the model. 23 | Data for each row is exposed like for a delegate of a QML View. 24 | 25 | This expression is reevaluated for a row every time its model data changes. 26 | When an external property (not \c index or in \c model) the expression depends on changes, the expression is reevaluated for every row of the source model. 27 | To capture the properties the expression depends on, the expression is first executed with invalid data and each property access is detected by the QML engine. 28 | This means that if a property is not accessed because of a conditional, it won't be captured and the expression won't be reevaluted when this property changes. 29 | 30 | A workaround to this problem is to access all the properties the expressions depends unconditionally at the beggining of the expression. 31 | */ 32 | const QQmlScriptString& ExpressionFilter::expression() const 33 | { 34 | return m_scriptString; 35 | } 36 | 37 | void ExpressionFilter::setExpression(const QQmlScriptString& scriptString) 38 | { 39 | if (m_scriptString == scriptString) 40 | return; 41 | 42 | m_scriptString = scriptString; 43 | updateExpression(); 44 | 45 | Q_EMIT expressionChanged(); 46 | invalidate(); 47 | } 48 | 49 | void ExpressionFilter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) 50 | { 51 | updateContext(proxyModel); 52 | } 53 | 54 | bool ExpressionFilter::filterRow(const QModelIndex& sourceIndex, const QQmlSortFilterProxyModel& proxyModel) const 55 | { 56 | if (!m_scriptString.isEmpty()) { 57 | QVariantMap modelMap; 58 | QHash roles = proxyModel.roleNames(); 59 | 60 | QQmlContext context(qmlContext(this)); 61 | auto addToContext = [&] (const QString &name, const QVariant& value) { 62 | context.setContextProperty(name, value); 63 | modelMap.insert(name, value); 64 | }; 65 | 66 | for (auto it = roles.cbegin(); it != roles.cend(); ++it) 67 | addToContext(it.value(), proxyModel.sourceData(sourceIndex, it.key())); 68 | addToContext("index", sourceIndex.row()); 69 | 70 | context.setContextProperty("model", modelMap); 71 | 72 | QQmlExpression expression(m_scriptString, &context); 73 | QVariant variantResult = expression.evaluate(); 74 | 75 | if (expression.hasError()) { 76 | qWarning() << expression.error(); 77 | return true; 78 | } 79 | if (variantResult.canConvert()) { 80 | return variantResult.toBool(); 81 | } else { 82 | qWarning("%s:%i:%i : Can't convert result to bool", 83 | expression.sourceFile().toUtf8().data(), 84 | expression.lineNumber(), 85 | expression.columnNumber()); 86 | return true; 87 | } 88 | } 89 | return true; 90 | } 91 | 92 | void ExpressionFilter::updateContext(const QQmlSortFilterProxyModel& proxyModel) 93 | { 94 | delete m_context; 95 | m_context = new QQmlContext(qmlContext(this), this); 96 | // what about roles changes ? 97 | QVariantMap modelMap; 98 | 99 | auto addToContext = [&] (const QString &name, const QVariant& value) { 100 | m_context->setContextProperty(name, value); 101 | modelMap.insert(name, value); 102 | }; 103 | 104 | for (const QByteArray& roleName : proxyModel.roleNames().values()) 105 | addToContext(roleName, QVariant()); 106 | 107 | addToContext("index", -1); 108 | 109 | m_context->setContextProperty("model", modelMap); 110 | updateExpression(); 111 | } 112 | 113 | void ExpressionFilter::updateExpression() 114 | { 115 | if (!m_context) 116 | return; 117 | 118 | delete m_expression; 119 | m_expression = new QQmlExpression(m_scriptString, m_context, 0, this); 120 | connect(m_expression, &QQmlExpression::valueChanged, this, &ExpressionFilter::invalidate); 121 | m_expression->setNotifyOnValueChanged(true); 122 | m_expression->evaluate(); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/domainsources/browserhistorydb.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | 18 | #include "browserhistorydb.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | BrowserHistoryDb::BrowserHistoryDb(QObject *parent) 27 | : QObject{parent} 28 | { 29 | m_domains = new domainCountListModel(this); 30 | } 31 | 32 | bool BrowserHistoryDb::openDb(const QUrl path) 33 | { 34 | 35 | _db = QSqlDatabase::addDatabase("QSQLITE"); 36 | setDbFileName(path.toLocalFile()); 37 | _db.setDatabaseName(dbFileName()); 38 | 39 | if(!_db.open()) { 40 | setLastDbError(_db.lastError().text()); 41 | return false; 42 | } else { 43 | return true; 44 | } 45 | 46 | } 47 | 48 | void BrowserHistoryDb::getHostnamesFromDb() 49 | { 50 | if(!_db.isOpen()) 51 | return; 52 | 53 | QStringList hostnames; 54 | QHash countsHash; 55 | QList countsList; 56 | 57 | QSqlQuery query; 58 | if(isFirefox()) 59 | query.prepare(firefoxQuery()); 60 | else 61 | query.prepare(chromeQuery()); 62 | 63 | query.exec(); 64 | 65 | while(query.next()) { 66 | QString host = query.value(0).toString(); 67 | int count = query.value(1).toInt(); 68 | if(isFirefox()) { 69 | host.chop(1); // remove dot 70 | std::reverse(host.begin(), host.end()); 71 | } 72 | hostnames.push_back(host); 73 | countsHash[host] = count; 74 | } 75 | setLastDbError(query.lastError().text()); 76 | setHostnames(hostnames); 77 | 78 | QHash::iterator i; 79 | for(i = countsHash.begin(); i != countsHash.end(); ++i) { 80 | countsList.push_back({i.key(), i.value()}); 81 | } 82 | 83 | std::sort(countsList.begin(), countsList.end(), [](const QIntPair& a, const QIntPair& b) { return a.second > b.second; }); 84 | m_domains->updateFromQList(countsList); 85 | } 86 | 87 | QString BrowserHistoryDb::firefoxQuery() 88 | { 89 | // Most visited domain names, hostnames are reversed. 90 | return "SELECT p.rev_host, count(*)" 91 | " FROM moz_places p" 92 | " JOIN moz_historyvisits v ON v.place_id = p.id" 93 | " WHERE p.url LIKE 'https%' " 94 | " GROUP BY p.rev_host ORDER BY count(*) desc ;"; 95 | } 96 | 97 | QString BrowserHistoryDb::chromeQuery() 98 | { 99 | return "SELECT" 100 | " SUBSTR(SUBSTR(url, INSTR(url, '//') + 2), 0, INSTR(SUBSTR(url, INSTR(url, '//') + 2), '/')) AS domain," 101 | " SUM(visit_count) AS total_visits" 102 | " FROM urls" 103 | " WHERE url LIKE 'https%'" 104 | " GROUP BY domain" 105 | " ORDER BY total_visits DESC;"; 106 | } 107 | 108 | bool BrowserHistoryDb::isFirefox() const 109 | { 110 | return m_isFirefox; 111 | } 112 | 113 | void BrowserHistoryDb::setIsFirefox(bool newIsFirefox) 114 | { 115 | if (m_isFirefox == newIsFirefox) 116 | return; 117 | m_isFirefox = newIsFirefox; 118 | emit isFirefoxChanged(); 119 | } 120 | 121 | 122 | 123 | 124 | QStringList BrowserHistoryDb::hostnames() const 125 | { 126 | return m_hostnames; 127 | } 128 | 129 | void BrowserHistoryDb::setHostnames(const QStringList &newHostnames) 130 | { 131 | if (m_hostnames == newHostnames) 132 | return; 133 | m_hostnames = newHostnames; 134 | emit hostnamesChanged(); 135 | } 136 | 137 | QString BrowserHistoryDb::dbFileName() const 138 | { 139 | return m_dbFileName; 140 | } 141 | 142 | void BrowserHistoryDb::setDbFileName(const QString &newDbFileName) 143 | { 144 | if (m_dbFileName == newDbFileName) 145 | return; 146 | m_dbFileName = newDbFileName; 147 | emit dbFileNameChanged(); 148 | } 149 | 150 | QString BrowserHistoryDb::lastDbError() const 151 | { 152 | return m_lastDbError; 153 | } 154 | 155 | void BrowserHistoryDb::setLastDbError(const QString &newLastDbError) 156 | { 157 | if (m_lastDbError == newLastDbError) 158 | return; 159 | m_lastDbError = newLastDbError; 160 | emit lastDbErrorChanged(); 161 | } 162 | 163 | domainCountListModel *BrowserHistoryDb::domains() const 164 | { 165 | return m_domains; 166 | } 167 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/README.md: -------------------------------------------------------------------------------- 1 | SortFilterProxyModel 2 | ==================== 3 | 4 | SortFilterProxyModel is an implementation of `QSortFilterProxyModel` conveniently exposed for QML. 5 | 6 | This is a fork that combines the Qt6 port of [MenloSystems/SortFilterProxyModel](https://github.com/MenloSystems/SortFilterProxyModel) with a few of my own fixes to compile and run under both Qt 5.15 and 6.5. 7 | A github actions workflow compiles the code to make sure it keeps compiling on both versions. 8 | 9 | Install 10 | ------- 11 | ##### With [qpm](https://qpm.io) : 12 | 1. `qpm install fr.grecko.sortfilterproxymodel` 13 | 2. add `include(vendor/vendor.pri)` in your .pro if it is not already done 14 | 3. `import SortFilterProxyModel 0.2` to use this library in your QML files 15 | 16 | ##### Without qpm : 17 | 1. clone or download this repository 18 | 2. * `qmake` add `include (/SortFilterProxyModel.pri)` in your `.pro` 19 | * `CMake` add $ to the sources of your executable target in your cmake project 20 | 3. `import SortFilterProxyModel 0.2` to use this library in your QML files 21 | 22 | Sample Usage 23 | ------------ 24 | 25 | - You can do simple filtering and sorting with SortFilterProxyModel: 26 | ```qml 27 | import QtQuick 2.2 28 | import QtQuick.Controls 1.2 29 | import SortFilterProxyModel 0.2 30 | 31 | ApplicationWindow { 32 | visible: true 33 | width: 640 34 | height: 480 35 | 36 | ListModel { 37 | id: personModel 38 | ListElement { 39 | firstName: "Erwan" 40 | lastName: "Castex" 41 | favorite: true 42 | } 43 | // ... 44 | } 45 | 46 | TextField { 47 | id: textField 48 | anchors { top: parent.top; left: parent.left; right: parent.right } 49 | height: implicitHeight 50 | } 51 | 52 | SortFilterProxyModel { 53 | id: personProxyModel 54 | sourceModel: personModel 55 | filters: RegExpFilter { 56 | roleName: "lastName" 57 | pattern: textField.text 58 | caseSensitivity: Qt.CaseInsensitive 59 | } 60 | sorters: StringSorter { roleName: "firstName" } 61 | } 62 | 63 | ListView { 64 | anchors { top: textField.bottom; bottom: parent.bottom; left: parent.left; right: parent.right } 65 | model: personProxyModel 66 | delegate: Text { text: model.firstName + " " + model.lastName} 67 | } 68 | } 69 | ``` 70 | Here the `ListView` will only show elements that contains the content of the `TextField` in their `lastName` role. 71 | 72 | - But you can also achieve more complex filtering or sorting with multiple `filters` and `sorters`: 73 | ```qml 74 | SortFilterProxyModel { 75 | id: personProxyModel 76 | sourceModel: personModel 77 | filters: [ 78 | ValueFilter { 79 | enabled: onlyShowFavoritesCheckbox.checked 80 | roleName: "favorite" 81 | value: true 82 | }, 83 | AnyOf { 84 | RegExpFilter { 85 | roleName: "lastName" 86 | pattern: textField.text 87 | caseSensitivity: Qt.CaseInsensitive 88 | } 89 | RegExpFilter { 90 | roleName: "firstName" 91 | pattern: textField.text 92 | caseSensitivity: Qt.CaseInsensitive 93 | } 94 | } 95 | ] 96 | sorters: [ 97 | RoleSorter { roleName: "favorite"; sortOrder: Qt.DescendingOrder }, 98 | StringSorter { roleName: "firstName" }, 99 | StringSorter { roleName: "lastName" } 100 | ] 101 | } 102 | 103 | CheckBox { 104 | id:onlyShowFavoritesCheckbox 105 | } 106 | ``` 107 | This will show in the corresponding `ListView` only the elements where the `firstName` or the `lastName` match the text entered in the `textField`, and if the `onlyShowFavoritesCheckbox` is checked it will aditionnally filter the elements where `favorite` is `true`. 108 | The favorited elements will be shown first and all the elements are sorted by `firstName` and then `lastName`. 109 | 110 | Showcase Application 111 | -------------------- 112 | You can find an application showcasing this library here: https://github.com/oKcerG/SFPMShowcase 113 | 114 | License 115 | ------- 116 | This library is licensed under the MIT License. 117 | 118 | Documentation 119 | ------------- 120 | This component is a subclass of [`QSortFilterProxyModel`](http://doc.qt.io/qt-5/qsortfilterproxymodel.html), to use it, you need to set the `sourceModel` property to a [`QAbstractItemModel*`](http://doc.qt.io/qt-5/qabstractitemmodel.html) with correct role names. 121 | This means you can use it with custom c++ models or `ListModel`, but not with JavaScript models like arrays, integers or object instances. 122 | 123 | The complete documentation reference is available here: https://okcerg.github.io/SortFilterProxyModel/ 124 | 125 | Contributing 126 | ------------ 127 | Don't hesitate to open an issue about a suggestion, a bug, a lack of clarity in the documentation, etc. 128 | 129 | Pull requests are also welcome, if it's a important change you should open an issue first though. 130 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/proxyroles/switchrole.cpp: -------------------------------------------------------------------------------- 1 | #include "switchrole.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | #include "filters/filter.h" 4 | #include 5 | 6 | namespace qqsfpm { 7 | 8 | /*! 9 | \qmltype SwitchRole 10 | \inherits SingleRole 11 | \inqmlmodule SortFilterProxyModel 12 | \ingroup ProxyRoles 13 | \ingroup FilterContainer 14 | \brief A role using \l Filter to conditionnaly compute its data. 15 | 16 | A SwitchRole is a \l ProxyRole that computes its data with the help of \l Filter. 17 | Each top level filters specified in the \l SwitchRole is evaluated on the rows of the model, if a \l Filter evaluates to true, the data of the \l SwitchRole for this row will be the one of the attached \l {value} {SwitchRole.value} property. 18 | If no top level filters evaluate to true, the data will default to the one of the \l defaultRoleName (or the \l defaultValue if no \l defaultRoleName is specified). 19 | 20 | In the following example, the \c favoriteOrFirstNameSection role is equal to \c * if the \c favorite role of a row is true, otherwise it's the same as the \c firstName role : 21 | \code 22 | SortFilterProxyModel { 23 | sourceModel: contactModel 24 | proxyRoles: SwitchRole { 25 | name: "favoriteOrFirstNameSection" 26 | filters: ValueFilter { 27 | roleName: "favorite" 28 | value: true 29 | SwitchRole.value: "*" 30 | } 31 | defaultRoleName: "firstName" 32 | } 33 | } 34 | \endcode 35 | \sa FilterContainer 36 | */ 37 | SwitchRoleAttached::SwitchRoleAttached(QObject* parent) : QObject (parent) 38 | { 39 | if (!qobject_cast(parent)) 40 | qmlInfo(parent) << "SwitchRole must be attached to a Filter"; 41 | } 42 | 43 | /*! 44 | \qmlattachedproperty var SwitchRole::value 45 | 46 | This property attaches a value to a \l Filter. 47 | */ 48 | QVariant SwitchRoleAttached::value() const 49 | { 50 | return m_value; 51 | } 52 | 53 | void SwitchRoleAttached::setValue(QVariant value) 54 | { 55 | if (m_value == value) 56 | return; 57 | 58 | m_value = value; 59 | Q_EMIT valueChanged(); 60 | } 61 | 62 | /*! 63 | \qmlproperty string SwitchRole::defaultRoleName 64 | 65 | This property holds the default role name of the role. 66 | If no filter match a row, the data of this role will be the data of the role whose name is \c defaultRoleName. 67 | */ 68 | QString SwitchRole::defaultRoleName() const 69 | { 70 | return m_defaultRoleName; 71 | } 72 | 73 | void SwitchRole::setDefaultRoleName(const QString& defaultRoleName) 74 | { 75 | if (m_defaultRoleName == defaultRoleName) 76 | return; 77 | 78 | m_defaultRoleName = defaultRoleName; 79 | Q_EMIT defaultRoleNameChanged(); 80 | invalidate(); 81 | } 82 | 83 | /*! 84 | \qmlproperty var SwitchRole::defaultValue 85 | 86 | This property holds the default value of the role. 87 | If no filter match a row, and no \l defaultRoleName is set, the data of this role will be \c defaultValue. 88 | */ 89 | QVariant SwitchRole::defaultValue() const 90 | { 91 | return m_defaultValue; 92 | } 93 | 94 | void SwitchRole::setDefaultValue(const QVariant& defaultValue) 95 | { 96 | if (m_defaultValue == defaultValue) 97 | return; 98 | 99 | m_defaultValue = defaultValue; 100 | Q_EMIT defaultValueChanged(); 101 | invalidate(); 102 | } 103 | 104 | /*! 105 | \qmlproperty list SwitchRole::filters 106 | \default 107 | 108 | This property holds the list of filters for this proxy role. 109 | The data of this role will be equal to the attached \l {value} {SwitchRole.value} property of the first filter that matches the model row. 110 | 111 | \sa Filter, FilterContainer 112 | */ 113 | 114 | void SwitchRole::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) 115 | { 116 | for (Filter* filter : m_filters) 117 | filter->proxyModelCompleted(proxyModel); 118 | } 119 | 120 | SwitchRoleAttached* SwitchRole::qmlAttachedProperties(QObject* object) 121 | { 122 | return new SwitchRoleAttached(object); 123 | } 124 | 125 | QVariant SwitchRole::data(const QModelIndex &sourceIndex, const QQmlSortFilterProxyModel &proxyModel) 126 | { 127 | for (auto filter: m_filters) { 128 | if (!filter->enabled()) 129 | continue; 130 | if (filter->filterAcceptsRow(sourceIndex, proxyModel)) { 131 | auto attached = static_cast(qmlAttachedPropertiesObject(filter, false)); 132 | if (!attached) { 133 | qWarning() << "No SwitchRole.value provided for this filter" << filter; 134 | continue; 135 | } 136 | QVariant value = attached->value(); 137 | if (!value.isValid()) { 138 | qWarning() << "No SwitchRole.value provided for this filter" << filter; 139 | continue; 140 | } 141 | return value; 142 | } 143 | } 144 | if (!m_defaultRoleName.isEmpty()) 145 | return proxyModel.sourceData(sourceIndex, m_defaultRoleName); 146 | return m_defaultValue; 147 | } 148 | 149 | void SwitchRole::onFilterAppended(Filter *filter) 150 | { 151 | connect(filter, &Filter::invalidated, this, &SwitchRole::invalidate); 152 | auto attached = static_cast(qmlAttachedPropertiesObject(filter, true)); 153 | connect(attached, &SwitchRoleAttached::valueChanged, this, &SwitchRole::invalidate); 154 | invalidate(); 155 | } 156 | 157 | void SwitchRole::onFilterRemoved(Filter *filter) 158 | { 159 | Q_UNUSED(filter) 160 | invalidate(); 161 | } 162 | 163 | void SwitchRole::onFiltersCleared() 164 | { 165 | invalidate(); 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/sorters/expressionsorter.cpp: -------------------------------------------------------------------------------- 1 | #include "expressionsorter.h" 2 | #include "qqmlsortfilterproxymodel.h" 3 | #include 4 | 5 | namespace qqsfpm { 6 | 7 | /*! 8 | \qmltype ExpressionSorter 9 | \inherits Sorter 10 | \inqmlmodule SortFilterProxyModel 11 | \ingroup Sorters 12 | \brief Sorts row with a custom javascript expression. 13 | 14 | An ExpressionSorter is a \l Sorter allowing to implement custom sorting based on a javascript expression. 15 | */ 16 | 17 | /*! 18 | \qmlproperty expression ExpressionSorter::expression 19 | 20 | An expression to implement custom sorting. It must evaluate to a bool. 21 | It has the same syntax has a \l {http://doc.qt.io/qt-5/qtqml-syntax-propertybinding.html} {Property Binding}, except that it will be evaluated for each of the source model's rows. 22 | Model data is accessible for both rows with the \c modelLeft, and \c modelRight properties: 23 | 24 | \code 25 | sorters: ExpressionSorter { 26 | expression: { 27 | return modelLeft.someRole < modelRight.someRole; 28 | } 29 | } 30 | \endcode 31 | 32 | The \c index of the row is also available through \c modelLeft and \c modelRight. 33 | 34 | The expression should return \c true if the value of the left item is less than the value of the right item, otherwise returns false. 35 | 36 | This expression is reevaluated for a row every time its model data changes. 37 | When an external property (not \c index* or in \c model*) the expression depends on changes, the expression is reevaluated for every row of the source model. 38 | To capture the properties the expression depends on, the expression is first executed with invalid data and each property access is detected by the QML engine. 39 | This means that if a property is not accessed because of a conditional, it won't be captured and the expression won't be reevaluted when this property changes. 40 | 41 | A workaround to this problem is to access all the properties the expressions depends unconditionally at the beggining of the expression. 42 | */ 43 | const QQmlScriptString& ExpressionSorter::expression() const 44 | { 45 | return m_scriptString; 46 | } 47 | 48 | void ExpressionSorter::setExpression(const QQmlScriptString& scriptString) 49 | { 50 | if (m_scriptString == scriptString) 51 | return; 52 | 53 | m_scriptString = scriptString; 54 | updateExpression(); 55 | 56 | Q_EMIT expressionChanged(); 57 | invalidate(); 58 | } 59 | 60 | void ExpressionSorter::proxyModelCompleted(const QQmlSortFilterProxyModel& proxyModel) 61 | { 62 | updateContext(proxyModel); 63 | } 64 | 65 | bool evaluateBoolExpression(QQmlExpression& expression) 66 | { 67 | QVariant variantResult = expression.evaluate(); 68 | if (expression.hasError()) { 69 | qWarning() << expression.error(); 70 | return false; 71 | } 72 | if (variantResult.canConvert()) { 73 | return variantResult.toBool(); 74 | } else { 75 | qWarning("%s:%i:%i : Can't convert result to bool", 76 | expression.sourceFile().toUtf8().data(), 77 | expression.lineNumber(), 78 | expression.columnNumber()); 79 | return false; 80 | } 81 | } 82 | 83 | int ExpressionSorter::compare(const QModelIndex& sourceLeft, const QModelIndex& sourceRight, const QQmlSortFilterProxyModel& proxyModel) const 84 | { 85 | if (!m_scriptString.isEmpty()) { 86 | QVariantMap modelLeftMap, modelRightMap; 87 | QHash roles = proxyModel.roleNames(); 88 | 89 | QQmlContext context(qmlContext(this)); 90 | 91 | for (auto it = roles.cbegin(); it != roles.cend(); ++it) { 92 | modelLeftMap.insert(it.value(), proxyModel.sourceData(sourceLeft, it.key())); 93 | modelRightMap.insert(it.value(), proxyModel.sourceData(sourceRight, it.key())); 94 | } 95 | modelLeftMap.insert("index", sourceLeft.row()); 96 | modelRightMap.insert("index", sourceRight.row()); 97 | 98 | QQmlExpression expression(m_scriptString, &context); 99 | 100 | context.setContextProperty("modelLeft", modelLeftMap); 101 | context.setContextProperty("modelRight", modelRightMap); 102 | if (evaluateBoolExpression(expression)) 103 | return -1; 104 | 105 | context.setContextProperty("modelLeft", modelRightMap); 106 | context.setContextProperty("modelRight", modelLeftMap); 107 | if (evaluateBoolExpression(expression)) 108 | return 1; 109 | } 110 | return 0; 111 | } 112 | 113 | void ExpressionSorter::updateContext(const QQmlSortFilterProxyModel& proxyModel) 114 | { 115 | delete m_context; 116 | m_context = new QQmlContext(qmlContext(this), this); 117 | 118 | QVariantMap modelLeftMap, modelRightMap; 119 | // what about roles changes ? 120 | 121 | for (const QByteArray& roleName : proxyModel.roleNames().values()) { 122 | modelLeftMap.insert(roleName, QVariant()); 123 | modelRightMap.insert(roleName, QVariant()); 124 | } 125 | modelLeftMap.insert("index", -1); 126 | modelRightMap.insert("index", -1); 127 | 128 | m_context->setContextProperty("modelLeft", modelLeftMap); 129 | m_context->setContextProperty("modelRight", modelRightMap); 130 | 131 | updateExpression(); 132 | } 133 | 134 | void ExpressionSorter::updateExpression() 135 | { 136 | if (!m_context) 137 | return; 138 | 139 | delete m_expression; 140 | m_expression = new QQmlExpression(m_scriptString, m_context, 0, this); 141 | connect(m_expression, &QQmlExpression::valueChanged, this, &ExpressionSorter::invalidate); 142 | m_expression->setNotifyOnValueChanged(true); 143 | m_expression->evaluate(); 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/thirdparty/SortFilterProxyModel/qqmlsortfilterproxymodel.h: -------------------------------------------------------------------------------- 1 | #ifndef QQMLSORTFILTERPROXYMODEL_H 2 | #define QQMLSORTFILTERPROXYMODEL_H 3 | 4 | #include 5 | #include 6 | #include "filters/filtercontainer.h" 7 | #include "sorters/sortercontainer.h" 8 | #include "proxyroles/proxyrolecontainer.h" 9 | 10 | namespace qqsfpm { 11 | 12 | class QQmlSortFilterProxyModel : public QSortFilterProxyModel, 13 | public QQmlParserStatus, 14 | public FilterContainer, 15 | public SorterContainer, 16 | public ProxyRoleContainer 17 | { 18 | Q_OBJECT 19 | Q_INTERFACES(QQmlParserStatus) 20 | Q_INTERFACES(qqsfpm::FilterContainer) 21 | Q_INTERFACES(qqsfpm::SorterContainer) 22 | Q_INTERFACES(qqsfpm::ProxyRoleContainer) 23 | 24 | Q_PROPERTY(int count READ count NOTIFY countChanged) 25 | Q_PROPERTY(bool delayed READ delayed WRITE setDelayed NOTIFY delayedChanged) 26 | 27 | Q_PROPERTY(QString filterRoleName READ filterRoleName WRITE setFilterRoleName NOTIFY filterRoleNameChanged) 28 | Q_PROPERTY(QString filterPattern READ filterPattern WRITE setFilterPattern NOTIFY filterPatternChanged) 29 | Q_PROPERTY(QVariant filterValue READ filterValue WRITE setFilterValue NOTIFY filterValueChanged) 30 | 31 | Q_PROPERTY(QString sortRoleName READ sortRoleName WRITE setSortRoleName NOTIFY sortRoleNameChanged) 32 | Q_PROPERTY(bool ascendingSortOrder READ ascendingSortOrder WRITE setAscendingSortOrder NOTIFY ascendingSortOrderChanged) 33 | 34 | Q_PROPERTY(QQmlListProperty filters READ filtersListProperty) 35 | Q_PROPERTY(QQmlListProperty sorters READ sortersListProperty) 36 | Q_PROPERTY(QQmlListProperty proxyRoles READ proxyRolesListProperty) 37 | 38 | public: 39 | QQmlSortFilterProxyModel(QObject* parent = 0); 40 | 41 | int count() const; 42 | 43 | bool delayed() const; 44 | void setDelayed(bool delayed); 45 | 46 | const QString& filterRoleName() const; 47 | void setFilterRoleName(const QString& filterRoleName); 48 | 49 | QString filterPattern() const; 50 | void setFilterPattern(const QString& filterPattern); 51 | 52 | const QVariant& filterValue() const; 53 | void setFilterValue(const QVariant& filterValue); 54 | 55 | const QString& sortRoleName() const; 56 | void setSortRoleName(const QString& sortRoleName); 57 | 58 | bool ascendingSortOrder() const; 59 | void setAscendingSortOrder(bool ascendingSortOrder); 60 | 61 | void classBegin() override; 62 | void componentComplete() override; 63 | 64 | QVariant sourceData(const QModelIndex& sourceIndex, const QString& roleName) const; 65 | QVariant sourceData(const QModelIndex& sourceIndex, int role) const; 66 | 67 | QVariant data(const QModelIndex& index, int role) const override; 68 | QHash roleNames() const override; 69 | 70 | Q_INVOKABLE int roleForName(const QString& roleName) const; 71 | 72 | Q_INVOKABLE QVariantMap get(int row) const; 73 | Q_INVOKABLE QVariant get(int row, const QString& roleName) const; 74 | 75 | Q_INVOKABLE QModelIndex mapToSource(const QModelIndex& proxyIndex) const override; 76 | Q_INVOKABLE int mapToSource(int proxyRow) const; 77 | Q_INVOKABLE QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override; 78 | Q_INVOKABLE int mapFromSource(int sourceRow) const; 79 | 80 | void setSourceModel(QAbstractItemModel *sourceModel) override; 81 | 82 | Q_SIGNALS: 83 | void countChanged(); 84 | void delayedChanged(); 85 | 86 | void filterRoleNameChanged(); 87 | void filterPatternChanged(); 88 | void filterValueChanged(); 89 | 90 | void sortRoleNameChanged(); 91 | void ascendingSortOrderChanged(); 92 | 93 | protected: 94 | bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; 95 | bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override; 96 | 97 | protected Q_SLOTS: 98 | void resetInternalData(); 99 | 100 | private Q_SLOTS: 101 | void queueInvalidateFilter(); 102 | void invalidateFilter(); 103 | void queueInvalidate(); 104 | void invalidate(); 105 | void updateRoleNames(); 106 | void updateFilterRole(); 107 | void updateSortRole(); 108 | void updateRoles(); 109 | void initRoles(); 110 | void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles); 111 | void queueInvalidateProxyRoles(); 112 | void invalidateProxyRoles(); 113 | 114 | private: 115 | QVariantMap modelDataMap(const QModelIndex& modelIndex) const; 116 | 117 | void onFilterAppended(Filter* filter) override; 118 | void onFilterRemoved(Filter* filter) override; 119 | void onFiltersCleared() override; 120 | 121 | void onSorterAppended(Sorter* sorter) override; 122 | void onSorterRemoved(Sorter* sorter) override; 123 | void onSortersCleared() override; 124 | 125 | void onProxyRoleAppended(ProxyRole *proxyRole) override; 126 | void onProxyRoleRemoved(ProxyRole *proxyRole) override; 127 | void onProxyRolesCleared() override; 128 | 129 | bool m_delayed; 130 | QString m_filterRoleName; 131 | QVariant m_filterValue; 132 | QString m_sortRoleName; 133 | bool m_ascendingSortOrder = true; 134 | bool m_completed = false; 135 | QHash m_roleNames; 136 | QHash> m_proxyRoleMap; 137 | QVector m_proxyRoleNumbers; 138 | 139 | bool m_invalidateFilterQueued = false; 140 | bool m_invalidateQueued = false; 141 | bool m_invalidateProxyRolesQueued = false; 142 | }; 143 | 144 | } 145 | 146 | #endif // QQMLSORTFILTERPROXYMODEL_H 147 | -------------------------------------------------------------------------------- /src/ca/certificate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | struct SubjectInfo { 27 | Q_GADGET 28 | Q_PROPERTY(QString country MEMBER country) 29 | Q_PROPERTY(QString state MEMBER state) 30 | Q_PROPERTY(QString locality MEMBER locality) 31 | Q_PROPERTY(QString organization MEMBER organization) 32 | Q_PROPERTY(QString organizationalUnit MEMBER organizationalUnit) 33 | Q_PROPERTY(QString commonName MEMBER commonName) 34 | Q_PROPERTY(QString distinguishedName MEMBER distinguishedName) 35 | Q_PROPERTY(QString email MEMBER email) 36 | Q_PROPERTY(QString serial MEMBER serial) 37 | 38 | public: 39 | QString country; 40 | QString state; 41 | QString locality; 42 | QString organization; 43 | QString organizationalUnit; 44 | QString commonName; 45 | QString distinguishedName; 46 | QString email; 47 | QString serial; 48 | 49 | 50 | 51 | 52 | QString toQString() const { 53 | return QString::fromStdString(toString()); 54 | } 55 | 56 | std::string toString() const { 57 | std::string result; 58 | if(!country.isEmpty()) 59 | result.append("C : " + country.toStdString() + "\n"); 60 | if(!state.isEmpty()) 61 | result.append("ST : " + state.toStdString() + "\n"); 62 | if(!locality.isEmpty()) 63 | result.append("L : " + locality.toStdString() + "\n"); 64 | if(!organization.isEmpty()) 65 | result.append("O : " + organization.toStdString() + "\n"); 66 | if(!organizationalUnit.isEmpty()) 67 | result.append("OU : " + organizationalUnit.toStdString() + "\n"); 68 | if(!commonName.isEmpty()) 69 | result.append("CN : " + commonName.toStdString() + "\n"); 70 | if(!distinguishedName.isEmpty()) 71 | result.append("DN : " + distinguishedName.toStdString() + "\n"); 72 | if(!email.isEmpty()) 73 | result.append("E : " + email.toStdString() + "\n"); 74 | if(!serial.isEmpty()) 75 | result.append("SN : " + serial.toStdString() + "\n"); 76 | 77 | if(result.empty()) 78 | result.append("Empty Subject\n"); 79 | 80 | 81 | return result; 82 | } 83 | 84 | friend std::ostream& operator<<(std::ostream& os, const SubjectInfo& subjectInfo) { 85 | os << subjectInfo.toString(); 86 | return os; 87 | } 88 | 89 | bool operator==(const SubjectInfo& other) const { 90 | return ( 91 | country == other.country && 92 | state == other.state && 93 | locality == other.locality && 94 | organization == other.organization && 95 | organizationalUnit == other.organizationalUnit && 96 | commonName == other.commonName && 97 | distinguishedName == other.distinguishedName && 98 | email == other.email && 99 | serial == other.serial 100 | ); 101 | } 102 | 103 | bool operator!=(const SubjectInfo& other) const { 104 | return !(*this == other); 105 | } 106 | 107 | bool operator<(const SubjectInfo& other) const { 108 | // Compare each member lexicographically 109 | return ( 110 | country < other.country || 111 | (country == other.country && state < other.state) || 112 | (country == other.country && state == other.state && locality < other.locality) || 113 | (country == other.country && state == other.state && locality == other.locality && 114 | organization < other.organization) || 115 | (country == other.country && state == other.state && locality == other.locality && 116 | organization == other.organization && organizationalUnit < other.organizationalUnit) || 117 | (country == other.country && state == other.state && locality == other.locality && 118 | organization == other.organization && organizationalUnit == other.organizationalUnit && 119 | commonName < other.commonName) || 120 | (country == other.country && state == other.state && locality == other.locality && 121 | organization == other.organization && organizationalUnit == other.organizationalUnit && 122 | commonName == other.commonName && distinguishedName < other.distinguishedName) || 123 | (country == other.country && state == other.state && locality == other.locality && 124 | organization == other.organization && organizationalUnit == other.organizationalUnit && 125 | commonName == other.commonName && distinguishedName == other.distinguishedName && 126 | email < other.email) || 127 | (country == other.country && state == other.state && locality == other.locality && 128 | organization == other.organization && organizationalUnit == other.organizationalUnit && 129 | commonName == other.commonName && distinguishedName == other.distinguishedName && 130 | email == other.email && serial < other.serial) 131 | ); 132 | } 133 | }; 134 | Q_DECLARE_METATYPE(SubjectInfo); 135 | 136 | struct Certificate { 137 | Q_GADGET 138 | public: 139 | QString subject; 140 | SubjectInfo subjectInfo; 141 | QString issuer; 142 | SubjectInfo issuerInfo; 143 | QDateTime validFromDate; 144 | QDateTime validUntilDate; 145 | int count = 0; 146 | bool isSelfSigned = false; 147 | bool isCA = false; 148 | bool isSystemTrustedRootCA = false; 149 | QStringList domains; // domain that was in user provided history 150 | QStringList subjectAlternativeNames; // all domains that cert has 151 | QStringList errors; 152 | QSslCertificate _actualCert; 153 | 154 | bool operator==(const Certificate& other) const { 155 | return (subject == other.subject); 156 | } 157 | 158 | bool operator!=(const Certificate& other) const { 159 | return !(*this == other); 160 | } 161 | 162 | bool operator<(const Certificate& other) const { 163 | if(subject == other.subject) 164 | return count < other.count; 165 | else 166 | return subject.localeAwareCompare(other.subject); 167 | } 168 | 169 | QString toQString() const { 170 | return QString::fromStdString(toString()); 171 | } 172 | 173 | std::string toString() const { 174 | std::stringstream ss; 175 | ss << "Count: " << count << "; \n" 176 | << "Subject: " << subject.toStdString() << "; \n"; 177 | ss << subjectInfo.toString() << "\n\n"; 178 | 179 | ss << "Valid From: " << validFromDate.toString().toStdString() << "\n"; 180 | ss << "Valid Until:" << validUntilDate.toString().toStdString() << "\n"; 181 | 182 | ss << "Issuer: " << issuer.toStdString() << "; \n"; 183 | ss << issuerInfo.toString() << "\n\n"; 184 | 185 | ss << "CA: " << (isCA ? "true" : "false") << "\n"; 186 | 187 | ss << "Self Signed: " << (isSelfSigned ? "true" : "false") << "\n"; 188 | 189 | ss << "Trusted Root CA:" << (isSystemTrustedRootCA ? "true" : "false") << "\n"; 190 | 191 | if(!domains.isEmpty()) { 192 | ss << "User Domains: "; 193 | 194 | for (const auto& domain : domains) { 195 | ss << domain.toStdString() << " "; 196 | } 197 | ss << "; \n"; 198 | } 199 | 200 | if(!subjectAlternativeNames.isEmpty()) { 201 | ss << "subjectAltNames: "; 202 | 203 | for (const auto& san : subjectAlternativeNames) { 204 | ss << san.toStdString() << " "; 205 | } 206 | ss << "; \n"; 207 | } 208 | 209 | if(!errors.isEmpty()) { 210 | ss << "Errors: "; 211 | 212 | for (const auto& err : errors) { 213 | ss << err.toStdString() << " "; 214 | } 215 | ss << "; \n"; 216 | } 217 | 218 | return ss.str(); 219 | } 220 | 221 | friend std::ostream& operator<<(std::ostream& os, const Certificate& issuer) { 222 | os << issuer.toString(); 223 | return os; 224 | } 225 | 226 | }; 227 | 228 | Q_DECLARE_METATYPE(Certificate); 229 | -------------------------------------------------------------------------------- /src/listmodel/genericlistmodel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | 18 | #pragma once 19 | 20 | #include "qabstractlistmodelwithrowcountsignal.h" 21 | 22 | template 23 | class GenericListModel : public QAbstractListModelWithRowCountSignal 24 | { 25 | // Q_OBJECT macro is not supported in template classes. 26 | // This is not a problem, but qmlRegisterType is not possible with E.G. GenericListModel. 27 | // A derived class MyQObjectListModel : public GenericListModel is still necessary. 28 | public: 29 | GenericListModel(QObject *parent = nullptr) : 30 | QAbstractListModelWithRowCountSignal(parent) { } 31 | 32 | void clear(); 33 | void updateFromVector(const std::vector &newObjects); 34 | void updateFromQList(const QList &newObjects); 35 | void addRow(const TObject&); 36 | void updateRow(int rowNr, const TObject&); 37 | QHash roleNames() const override; 38 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 39 | bool setData(const QModelIndex &index, const QVariant &value, int role) override; 40 | int rowCount(const QModelIndex &parent = QModelIndex()) const override; 41 | 42 | virtual void move(int from, int to); 43 | int addSelector(QByteArray name, std::function selector); 44 | int addSelector(QByteArray name, std::function selector); 45 | int addSelector(QByteArray name, std::function selector, std::function valueSetter); 46 | int addSelector(QByteArray name, std::function selector, std::function valueSetter); 47 | QVariant dataByRoleName(const QModelIndex &index, const QString &name) const; 48 | void resync(); 49 | int getRoleIdByName(const QString &name) const; 50 | QVariantMap getRowByField(const QString &fieldName, const QVariant &value) const; 51 | 52 | protected: 53 | QList m_listObjects; 54 | QMap>> m_selectors; 55 | QMap>> m_valueSetters; 56 | }; 57 | 58 | template 59 | void GenericListModel::updateFromVector(const std::vector &newObjects) 60 | { 61 | beginResetModel(); 62 | m_listObjects.clear(); 63 | for (const TObject &item : newObjects) 64 | { 65 | m_listObjects << item; 66 | } 67 | endResetModel(); 68 | } 69 | 70 | 71 | template 72 | void GenericListModel::updateFromQList(const QList &newObjects) 73 | { 74 | beginResetModel(); 75 | m_listObjects = newObjects; 76 | endResetModel(); 77 | } 78 | 79 | template 80 | void GenericListModel::clear() 81 | { 82 | beginResetModel(); 83 | m_listObjects.clear(); 84 | endResetModel(); 85 | } 86 | 87 | template 88 | QHash GenericListModel::roleNames() const 89 | { 90 | QHash roles; 91 | for (auto it = m_selectors.begin(); it != m_selectors.end(); ++it) 92 | { 93 | roles[it.key()] = it->first; 94 | } 95 | return roles; 96 | } 97 | 98 | template 99 | QVariant GenericListModel::data(const QModelIndex &index, int role) const 100 | { 101 | if (!index.isValid()) 102 | { 103 | return QVariant(); 104 | } 105 | 106 | const TObject &object = m_listObjects[index.row()]; 107 | auto selectorIterator = m_selectors.find(role); 108 | if (selectorIterator != m_selectors.end()) 109 | return selectorIterator->second(object, index); 110 | return QVariant(); 111 | } 112 | 113 | template 114 | bool GenericListModel::setData(const QModelIndex &index, const QVariant &value, int role) 115 | { 116 | if (!index.isValid()) 117 | return false; 118 | 119 | bool result = false; 120 | TObject &object = m_listObjects[index.row()]; 121 | auto valueSetterIterator = m_valueSetters.find(role); 122 | if (valueSetterIterator != m_valueSetters.end()) 123 | { 124 | result = valueSetterIterator->second(object, value); 125 | if (result) 126 | emit dataChanged(index, index, {role}); 127 | } 128 | return result; 129 | } 130 | 131 | template 132 | int GenericListModel::rowCount(const QModelIndex &index) const 133 | { 134 | if (index.isValid()) 135 | { 136 | return 0; 137 | } 138 | return m_listObjects.count(); 139 | } 140 | 141 | template 142 | void GenericListModel::move(int from, int to) 143 | { 144 | m_listObjects.move(from, to); 145 | 146 | } 147 | 148 | template 149 | int GenericListModel::addSelector(QByteArray name, std::function selector) 150 | { 151 | std::function newselector = std::bind(selector, std::placeholders::_1); 152 | return addSelector(name, newselector); 153 | } 154 | 155 | template 156 | int GenericListModel::addSelector(QByteArray name, std::function selector, std::function valueSetter) 157 | { 158 | int roleNumber = addSelector(name, selector); 159 | m_valueSetters[roleNumber] = QPair> {name, valueSetter}; 160 | return roleNumber; 161 | } 162 | 163 | template 164 | int GenericListModel::addSelector(QByteArray name, std::function selector, std::function valueSetter) 165 | { 166 | int roleNumber = addSelector(name, selector); 167 | m_valueSetters[roleNumber] = QPair> {name, valueSetter}; 168 | return roleNumber; 169 | } 170 | 171 | template 172 | int GenericListModel::addSelector(QByteArray name, std::function selector) 173 | { 174 | int roleNumber = Qt::UserRole + m_selectors.size() + 1; 175 | m_selectors[roleNumber] = QPair> {name, selector}; 176 | return roleNumber; 177 | } 178 | 179 | template 180 | QVariant GenericListModel::dataByRoleName(const QModelIndex &index, const QString &roleName) const 181 | { 182 | if (!index.isValid()) 183 | { 184 | return QVariant(); 185 | } 186 | 187 | const TObject &object = m_listObjects[index.row()]; 188 | for (const QPair> &mapEntry : m_selectors) 189 | { 190 | if (mapEntry.first == roleName) 191 | return mapEntry.second(object, index); 192 | } 193 | return QVariant(); 194 | } 195 | 196 | template 197 | void GenericListModel::resync() 198 | { 199 | std::vector copiedListObjects; 200 | std::copy(m_listObjects.begin(), m_listObjects.end(), std::back_inserter(copiedListObjects)); 201 | updateFromVector(copiedListObjects); 202 | } 203 | 204 | 205 | template 206 | void GenericListModel::addRow(const TObject& newRow) 207 | { 208 | auto size = m_listObjects.size(); 209 | emit beginInsertRows(QModelIndex(), size, size); 210 | m_listObjects.push_back(newRow); 211 | emit endInsertRows(); 212 | } 213 | 214 | 215 | template 216 | void GenericListModel::updateRow(int rowNr, const TObject& newRow) 217 | { 218 | if (rowNr >= m_listObjects.size()) 219 | return; 220 | m_listObjects[rowNr] = newRow; 221 | emit dataChanged(index(rowNr), index(rowNr)); 222 | } 223 | 224 | 225 | template 226 | int GenericListModel::getRoleIdByName(const QString &name) const 227 | { 228 | QHash names = roleNames(); 229 | auto iter = std::find_if(names.begin(), names.end(), [name](QByteArray roleName) { return roleName == name; }); 230 | return iter == names.end() ? -1 : iter.key(); 231 | } 232 | 233 | template 234 | QVariantMap GenericListModel::getRowByField(const QString &fieldName, const QVariant &value) const 235 | { 236 | QVariantMap data; 237 | QModelIndexList matches = this->match(this->index(0, 0), getRoleIdByName(fieldName), value, 1, Qt::MatchExactly); 238 | 239 | if (matches.begin() == matches.end()) 240 | return data; 241 | 242 | const QModelIndex idx = *matches.begin(); 243 | 244 | if (!idx.isValid()) 245 | return data; 246 | 247 | const QHash rn = this->roleNames(); 248 | 249 | for (auto it = rn.begin(); it != rn.end(); ++it) 250 | data[it.value()] = idx.data(it.key()); 251 | 252 | return data; 253 | } 254 | -------------------------------------------------------------------------------- /src/ca/caprocessor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Remy van Elst https://raymii.org 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, version 3. 7 | * 8 | * This program is distributed in the hope that it will be useful, but 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | #include "caprocessor.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | CAProcessor::CAProcessor(QObject *parent) 31 | : QObject{parent} 32 | { 33 | 34 | } 35 | 36 | bool CAProcessor::isCA(const QSslCertificate& cert) 37 | { 38 | for(const auto& ext : cert.extensions()) { 39 | if(ext.oid() != "2.5.29.19") // basicConstraints 40 | continue; 41 | 42 | auto v = ext.value(); 43 | // QVariant containing QVariantMap... Using documentation example to iterate over it... 44 | if(!v.canConvert()) 45 | continue; 46 | 47 | QAssociativeIterable iterable = v.value(); 48 | QAssociativeIterable::const_iterator it = iterable.begin(); 49 | const QAssociativeIterable::const_iterator end = iterable.end(); 50 | for ( ; it != end; ++it) { 51 | if(it.key().toString() == "ca") { 52 | return it.value().toBool(); 53 | } 54 | } 55 | } 56 | return false; 57 | } 58 | 59 | void CAProcessor::ignoreSslErrors(QNetworkReply* reply) 60 | { 61 | if(reply) 62 | reply->ignoreSslErrors(); 63 | } 64 | QList CAProcessor::getCertificate(const QString& domain) 65 | { 66 | QList resultList; 67 | QNetworkAccessManager nam; 68 | QNetworkRequest request; 69 | request.setUrl("https://" + domain); 70 | request.setTransferTimeout(3500); 71 | request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.52"); 72 | request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::UserVerifiedRedirectPolicy); 73 | 74 | QEventLoop loop; 75 | QNetworkReply* reply = nam.get(request); 76 | 77 | QTimer::singleShot(4000, &loop, &QEventLoop::quit); // backup timeout 78 | connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit); 79 | connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); 80 | connect(&nam, &QNetworkAccessManager::sslErrors, &CAProcessor::ignoreSslErrors); 81 | loop.exec(); 82 | 83 | if(reply && (reply->error() > QNetworkReply::NoError && reply->error() <= QNetworkReply::UnknownNetworkError) ) { 84 | Certificate error; 85 | QString errorString = reply->errorString(); 86 | error.subject = domain + ": " + errorString ; 87 | error.domains.push_back(domain); 88 | error.errors.push_back(errorString); 89 | reply->deleteLater(); 90 | return {error}; 91 | } 92 | 93 | QList systemCerts = QSslConfiguration::systemCaCertificates(); 94 | // add last root cert if server did not sent it 95 | QList peerCertChain; 96 | if(reply) 97 | peerCertChain = reply->sslConfiguration().peerCertificateChain(); 98 | 99 | auto it = std::find_if(systemCerts.begin(), systemCerts.end(), [&peerCertChain](const QSslCertificate& sys){ return peerCertChain.last().issuerDisplayName() == sys.subjectDisplayName(); }); 100 | if(it != systemCerts.end()) 101 | peerCertChain.push_back(*it); 102 | 103 | for(auto it = peerCertChain.begin(); it != peerCertChain.end(); ++it) { 104 | QSslCertificate cert = *it; 105 | 106 | if(cert.isNull()) 107 | continue; 108 | 109 | Certificate result = parseQSslCertificateToCertificate(cert); 110 | 111 | result.domains.push_back(domain); 112 | 113 | if(std::find(systemCerts.begin(), systemCerts.end(), cert) != systemCerts.end()) 114 | result.isSystemTrustedRootCA = true; 115 | 116 | resultList.push_back(result); 117 | } 118 | 119 | 120 | reply->deleteLater(); 121 | 122 | return resultList; 123 | } 124 | 125 | 126 | 127 | void CAProcessor::extractSubject(const QSslCertificate& cert, Certificate& out_issuer) 128 | { 129 | QString C = cert.subjectInfo(QSslCertificate::CountryName).join(" "); 130 | if(!C.isEmpty()) { 131 | out_issuer.subject.append(C + " "); 132 | out_issuer.subjectInfo.country = C; 133 | } 134 | 135 | QString ST = cert.subjectInfo(QSslCertificate::StateOrProvinceName).join(" "); 136 | if(!ST.isEmpty()) { 137 | out_issuer.subject.append(ST + " "); 138 | out_issuer.subjectInfo.state = ST; 139 | } 140 | 141 | QString L = cert.subjectInfo(QSslCertificate::LocalityName).join(" "); 142 | if(!L.isEmpty()) { 143 | out_issuer.subject.append(L + " "); 144 | out_issuer.subjectInfo.locality = L; 145 | } 146 | 147 | QString O = cert.subjectInfo(QSslCertificate::Organization).join(" "); 148 | if(!O.isEmpty()) { 149 | out_issuer.subject.append(O + " "); 150 | out_issuer.subjectInfo.organization = O; 151 | } 152 | 153 | QString OU = cert.subjectInfo(QSslCertificate::OrganizationalUnitName).join(" "); 154 | if(!OU.isEmpty()) { 155 | out_issuer.subject.append(OU + " "); 156 | out_issuer.subjectInfo.organizationalUnit = OU; 157 | } 158 | 159 | QString CN = cert.subjectInfo(QSslCertificate::CommonName).join(" "); 160 | if(!CN.isEmpty()) { 161 | out_issuer.subject.append(CN + " "); 162 | out_issuer.subjectInfo.commonName = CN; 163 | } 164 | 165 | QString DN = cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier).join(" "); 166 | if(!DN.isEmpty()) { 167 | out_issuer.subject.append(DN + " "); 168 | out_issuer.subjectInfo.distinguishedName = DN; 169 | } 170 | 171 | QString E = cert.subjectInfo(QSslCertificate::EmailAddress).join(" "); 172 | if(!E.isEmpty()) { 173 | out_issuer.subject.append(E + " "); 174 | out_issuer.subjectInfo.email = E; 175 | } 176 | 177 | QString SN = cert.subjectInfo(QSslCertificate::SerialNumber).join(" "); 178 | if(!SN.isEmpty()) { 179 | out_issuer.subject.append(SN + " "); 180 | out_issuer.subjectInfo.serial = SN; 181 | } 182 | 183 | } 184 | 185 | 186 | void CAProcessor::extractCertificate(const QSslCertificate& cert, Certificate& out_issuer) 187 | { 188 | QString C = cert.issuerInfo(QSslCertificate::CountryName).join(" "); 189 | if(!C.isEmpty()) { 190 | out_issuer.issuer.append(C + " "); 191 | out_issuer.issuerInfo.country = C; 192 | } 193 | 194 | QString ST = cert.issuerInfo(QSslCertificate::StateOrProvinceName).join(" "); 195 | if(!ST.isEmpty()) { 196 | out_issuer.issuer.append(ST + " "); 197 | out_issuer.issuerInfo.state = ST; 198 | } 199 | 200 | QString L = cert.issuerInfo(QSslCertificate::LocalityName).join(" "); 201 | if(!L.isEmpty()) { 202 | out_issuer.issuer.append(L + " "); 203 | out_issuer.issuerInfo.locality = L; 204 | } 205 | 206 | QString O = cert.issuerInfo(QSslCertificate::Organization).join(" "); 207 | if(!O.isEmpty()) { 208 | out_issuer.issuer.append(O + " "); 209 | out_issuer.issuerInfo.organization = O; 210 | } 211 | 212 | QString OU = cert.issuerInfo(QSslCertificate::OrganizationalUnitName).join(" "); 213 | if(!OU.isEmpty()) { 214 | out_issuer.issuer.append(OU + " "); 215 | out_issuer.issuerInfo.organizationalUnit = OU; 216 | } 217 | 218 | QString CN = cert.issuerInfo(QSslCertificate::CommonName).join(" "); 219 | if(!CN.isEmpty()) { 220 | out_issuer.issuer.append(CN + " "); 221 | out_issuer.issuerInfo.commonName = CN; 222 | } 223 | 224 | QString DN = cert.issuerInfo(QSslCertificate::DistinguishedNameQualifier).join(" "); 225 | if(!DN.isEmpty()) { 226 | out_issuer.issuer.append(DN + " "); 227 | out_issuer.issuerInfo.distinguishedName = DN; 228 | } 229 | 230 | QString E = cert.issuerInfo(QSslCertificate::EmailAddress).join(" "); 231 | if(!E.isEmpty()) { 232 | out_issuer.issuer.append(E + " "); 233 | out_issuer.issuerInfo.email = E; 234 | } 235 | 236 | QString SN = cert.issuerInfo(QSslCertificate::SerialNumber).join(" "); 237 | if(!SN.isEmpty()) { 238 | out_issuer.issuer.append(SN + " "); 239 | out_issuer.issuerInfo.serial = SN; 240 | } 241 | 242 | } 243 | 244 | 245 | 246 | Certificate CAProcessor::parseQSslCertificateToCertificate(const QSslCertificate& cert) 247 | { 248 | Certificate result; 249 | 250 | if(cert.isNull()) { 251 | result.subject = "Null Certificate!"; 252 | return result; 253 | } 254 | 255 | 256 | result._actualCert = cert; 257 | 258 | result.isCA = isCA(cert); 259 | 260 | result.isSelfSigned = cert.isSelfSigned(); 261 | 262 | 263 | for(const auto& san : cert.subjectAlternativeNames()) { 264 | result.subjectAlternativeNames.push_back(san); 265 | } 266 | 267 | 268 | result.validFromDate = cert.effectiveDate(); 269 | result.validUntilDate = cert.expiryDate(); 270 | 271 | extractSubject(cert, result); 272 | extractCertificate(cert, result); 273 | 274 | if(!result.subjectAlternativeNames.isEmpty()) 275 | result.isSystemTrustedRootCA = false; 276 | 277 | return result; 278 | } 279 | --------------------------------------------------------------------------------