File indexing completed on 2024-04-28 15:27:27

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2000 Yves Arrouye <yves@realnames.com>
0004     SPDX-FileCopyrightText: 2000, 2010 Dawit Alemayehu <adawit at kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "kurifilter.h"
0010 
0011 #include "hostinfo.h"
0012 
0013 #include <KIconLoader>
0014 #include <KService>
0015 #include <kio/global.h>
0016 
0017 #include <KPluginFactory>
0018 #include <KPluginMetaData>
0019 
0020 #include <QHashIterator>
0021 #include <QHostAddress>
0022 #include <QHostInfo>
0023 
0024 typedef QList<KUriFilterPlugin *> KUriFilterPluginList;
0025 typedef QMap<QString, KUriFilterSearchProvider *> SearchProviderMap;
0026 
0027 static QString lookupIconNameFor(const QUrl &url, KUriFilterData::UriTypes type)
0028 {
0029     QString iconName;
0030 
0031     switch (type) {
0032     case KUriFilterData::NetProtocol:
0033         iconName = KIO::iconNameForUrl(url);
0034         break;
0035     case KUriFilterData::Executable: {
0036         QString exeName = url.path();
0037         exeName.remove(0, exeName.lastIndexOf(QLatin1Char('/')) + 1); // strip path if given
0038         KService::Ptr service = KService::serviceByDesktopName(exeName);
0039         if (service && service->icon() != QLatin1String("unknown")) {
0040             iconName = service->icon();
0041         }
0042         // Try to find an icon with the same name as the binary (useful for non-kde apps)
0043         // Use iconPath rather than loadIcon() as the latter uses QPixmap (not threadsafe)
0044         else if (!KIconLoader::global()->iconPath(exeName, KIconLoader::NoGroup, true).isNull()) {
0045             iconName = exeName;
0046         } else
0047         // not found, use default
0048         {
0049             iconName = QStringLiteral("system-run");
0050         }
0051         break;
0052     }
0053     case KUriFilterData::Help: {
0054         iconName = QStringLiteral("khelpcenter");
0055         break;
0056     }
0057     case KUriFilterData::Shell: {
0058         iconName = QStringLiteral("konsole");
0059         break;
0060     }
0061     case KUriFilterData::Error:
0062     case KUriFilterData::Blocked: {
0063         iconName = QStringLiteral("error");
0064         break;
0065     }
0066     default:
0067         break;
0068     }
0069 
0070     return iconName;
0071 }
0072 
0073 class Q_DECL_HIDDEN KUriFilterSearchProvider::KUriFilterSearchProviderPrivate
0074 {
0075 public:
0076     KUriFilterSearchProviderPrivate()
0077     {
0078     }
0079     KUriFilterSearchProviderPrivate(const KUriFilterSearchProviderPrivate &other)
0080         : desktopEntryName(other.desktopEntryName)
0081         , iconName(other.iconName)
0082         , name(other.name)
0083         , keys(other.keys)
0084     {
0085     }
0086 
0087     QString desktopEntryName;
0088     QString iconName;
0089     QString name;
0090     QStringList keys;
0091 };
0092 
0093 KUriFilterSearchProvider::KUriFilterSearchProvider()
0094     : d(new KUriFilterSearchProvider::KUriFilterSearchProviderPrivate)
0095 {
0096 }
0097 
0098 KUriFilterSearchProvider::KUriFilterSearchProvider(const KUriFilterSearchProvider &other)
0099     : d(new KUriFilterSearchProvider::KUriFilterSearchProviderPrivate(*(other.d)))
0100 {
0101 }
0102 
0103 KUriFilterSearchProvider::~KUriFilterSearchProvider() = default;
0104 
0105 QString KUriFilterSearchProvider::desktopEntryName() const
0106 {
0107     return d->desktopEntryName;
0108 }
0109 
0110 QString KUriFilterSearchProvider::iconName() const
0111 {
0112     return d->iconName;
0113 }
0114 
0115 QString KUriFilterSearchProvider::name() const
0116 {
0117     return d->name;
0118 }
0119 
0120 QStringList KUriFilterSearchProvider::keys() const
0121 {
0122     return d->keys;
0123 }
0124 
0125 QString KUriFilterSearchProvider::defaultKey() const
0126 {
0127     if (d->keys.isEmpty()) {
0128         return QString();
0129     }
0130 
0131     return d->keys.first();
0132 }
0133 
0134 KUriFilterSearchProvider &KUriFilterSearchProvider::operator=(const KUriFilterSearchProvider &other)
0135 {
0136     d->desktopEntryName = other.d->desktopEntryName;
0137     d->iconName = other.d->iconName;
0138     d->keys = other.d->keys;
0139     d->name = other.d->name;
0140     return *this;
0141 }
0142 
0143 void KUriFilterSearchProvider::setDesktopEntryName(const QString &desktopEntryName)
0144 {
0145     d->desktopEntryName = desktopEntryName;
0146 }
0147 
0148 void KUriFilterSearchProvider::setIconName(const QString &iconName)
0149 {
0150     d->iconName = iconName;
0151 }
0152 
0153 void KUriFilterSearchProvider::setName(const QString &name)
0154 {
0155     d->name = name;
0156 }
0157 
0158 void KUriFilterSearchProvider::setKeys(const QStringList &keys)
0159 {
0160     d->keys = keys;
0161 }
0162 
0163 class KUriFilterDataPrivate
0164 {
0165 public:
0166     explicit KUriFilterDataPrivate(const QUrl &u, const QString &typedUrl)
0167         : checkForExecs(true)
0168         , wasModified(true)
0169         , uriType(KUriFilterData::Unknown)
0170         , searchFilterOptions(KUriFilterData::SearchFilterOptionNone)
0171         , url(u.adjusted(QUrl::NormalizePathSegments))
0172         , typedString(typedUrl)
0173     {
0174     }
0175 
0176     ~KUriFilterDataPrivate()
0177     {
0178     }
0179 
0180     void setData(const QUrl &u, const QString &typedUrl)
0181     {
0182         checkForExecs = true;
0183         wasModified = true;
0184         uriType = KUriFilterData::Unknown;
0185         searchFilterOptions = KUriFilterData::SearchFilterOptionNone;
0186 
0187         url = u.adjusted(QUrl::NormalizePathSegments);
0188         typedString = typedUrl;
0189 
0190         errMsg.clear();
0191         iconName.clear();
0192         absPath.clear();
0193         args.clear();
0194         searchTerm.clear();
0195         searchProvider.clear();
0196         searchTermSeparator = QChar();
0197         alternateDefaultSearchProvider.clear();
0198         alternateSearchProviders.clear();
0199         searchProviderMap.clear();
0200         defaultUrlScheme.clear();
0201     }
0202 
0203     KUriFilterDataPrivate(KUriFilterDataPrivate *data)
0204     {
0205         wasModified = data->wasModified;
0206         checkForExecs = data->checkForExecs;
0207         uriType = data->uriType;
0208         searchFilterOptions = data->searchFilterOptions;
0209 
0210         url = data->url;
0211         typedString = data->typedString;
0212 
0213         errMsg = data->errMsg;
0214         iconName = data->iconName;
0215         absPath = data->absPath;
0216         args = data->args;
0217         searchTerm = data->searchTerm;
0218         searchTermSeparator = data->searchTermSeparator;
0219         searchProvider = data->searchProvider;
0220         alternateDefaultSearchProvider = data->alternateDefaultSearchProvider;
0221         alternateSearchProviders = data->alternateSearchProviders;
0222         searchProviderMap = data->searchProviderMap;
0223         defaultUrlScheme = data->defaultUrlScheme;
0224     }
0225 
0226     bool checkForExecs;
0227     bool wasModified;
0228     KUriFilterData::UriTypes uriType;
0229     KUriFilterData::SearchFilterOptions searchFilterOptions;
0230 
0231     QUrl url;
0232     QString typedString;
0233     QString errMsg;
0234     QString iconName;
0235     QString absPath;
0236     QString args;
0237     QString searchTerm;
0238     QString searchProvider;
0239     QString alternateDefaultSearchProvider;
0240     QString defaultUrlScheme;
0241     QChar searchTermSeparator;
0242 
0243     QStringList alternateSearchProviders;
0244     QStringList searchProviderList;
0245     SearchProviderMap searchProviderMap;
0246 };
0247 
0248 KUriFilterData::KUriFilterData()
0249     : d(new KUriFilterDataPrivate(QUrl(), QString()))
0250 {
0251 }
0252 
0253 KUriFilterData::KUriFilterData(const QUrl &url)
0254     : d(new KUriFilterDataPrivate(url, url.toString()))
0255 {
0256 }
0257 
0258 KUriFilterData::KUriFilterData(const QString &url)
0259     : d(new KUriFilterDataPrivate(QUrl::fromUserInput(url), url))
0260 {
0261 }
0262 
0263 KUriFilterData::KUriFilterData(const KUriFilterData &other)
0264     : d(new KUriFilterDataPrivate(*other.d))
0265 {
0266 }
0267 
0268 KUriFilterData::~KUriFilterData() = default;
0269 
0270 QUrl KUriFilterData::uri() const
0271 {
0272     return d->url;
0273 }
0274 
0275 QString KUriFilterData::errorMsg() const
0276 {
0277     return d->errMsg;
0278 }
0279 
0280 KUriFilterData::UriTypes KUriFilterData::uriType() const
0281 {
0282     return d->uriType;
0283 }
0284 
0285 QString KUriFilterData::absolutePath() const
0286 {
0287     return d->absPath;
0288 }
0289 
0290 bool KUriFilterData::hasAbsolutePath() const
0291 {
0292     return !d->absPath.isEmpty();
0293 }
0294 
0295 QString KUriFilterData::argsAndOptions() const
0296 {
0297     return d->args;
0298 }
0299 
0300 bool KUriFilterData::hasArgsAndOptions() const
0301 {
0302     return !d->args.isEmpty();
0303 }
0304 
0305 bool KUriFilterData::checkForExecutables() const
0306 {
0307     return d->checkForExecs;
0308 }
0309 
0310 QString KUriFilterData::typedString() const
0311 {
0312     return d->typedString;
0313 }
0314 
0315 QString KUriFilterData::searchTerm() const
0316 {
0317     return d->searchTerm;
0318 }
0319 
0320 QChar KUriFilterData::searchTermSeparator() const
0321 {
0322     return d->searchTermSeparator;
0323 }
0324 
0325 QString KUriFilterData::searchProvider() const
0326 {
0327     return d->searchProvider;
0328 }
0329 
0330 QStringList KUriFilterData::preferredSearchProviders() const
0331 {
0332     return d->searchProviderList;
0333 }
0334 
0335 KUriFilterSearchProvider KUriFilterData::queryForSearchProvider(const QString &provider) const
0336 {
0337     const KUriFilterSearchProvider *searchProvider = d->searchProviderMap.value(provider);
0338 
0339     if (searchProvider) {
0340         return *(searchProvider);
0341     }
0342 
0343     return KUriFilterSearchProvider();
0344 }
0345 
0346 QString KUriFilterData::queryForPreferredSearchProvider(const QString &provider) const
0347 {
0348     const KUriFilterSearchProvider *searchProvider = d->searchProviderMap.value(provider);
0349     if (searchProvider) {
0350         return (searchProvider->defaultKey() % searchTermSeparator() % searchTerm());
0351     }
0352     return QString();
0353 }
0354 
0355 QStringList KUriFilterData::allQueriesForSearchProvider(const QString &provider) const
0356 {
0357     const KUriFilterSearchProvider *searchProvider = d->searchProviderMap.value(provider);
0358     if (searchProvider) {
0359         return searchProvider->keys();
0360     }
0361     return QStringList();
0362 }
0363 
0364 QString KUriFilterData::iconNameForPreferredSearchProvider(const QString &provider) const
0365 {
0366     const KUriFilterSearchProvider *searchProvider = d->searchProviderMap.value(provider);
0367     if (searchProvider) {
0368         return searchProvider->iconName();
0369     }
0370     return QString();
0371 }
0372 
0373 QStringList KUriFilterData::alternateSearchProviders() const
0374 {
0375     return d->alternateSearchProviders;
0376 }
0377 
0378 QString KUriFilterData::alternateDefaultSearchProvider() const
0379 {
0380     return d->alternateDefaultSearchProvider;
0381 }
0382 
0383 QString KUriFilterData::defaultUrlScheme() const
0384 {
0385     return d->defaultUrlScheme;
0386 }
0387 
0388 KUriFilterData::SearchFilterOptions KUriFilterData::searchFilteringOptions() const
0389 {
0390     return d->searchFilterOptions;
0391 }
0392 
0393 QString KUriFilterData::iconName()
0394 {
0395     if (d->wasModified) {
0396         d->iconName = lookupIconNameFor(d->url, d->uriType);
0397         d->wasModified = false;
0398     }
0399 
0400     return d->iconName;
0401 }
0402 
0403 void KUriFilterData::setData(const QUrl &url)
0404 {
0405     d->setData(url, url.toString());
0406 }
0407 
0408 void KUriFilterData::setData(const QString &url)
0409 {
0410     d->setData(QUrl(url), url);
0411 }
0412 
0413 bool KUriFilterData::setAbsolutePath(const QString &absPath)
0414 {
0415     // Since a malformed URL could possibly be a relative
0416     // URL we tag it as a possible local resource...
0417     if ((d->url.scheme().isEmpty() || d->url.isLocalFile())) {
0418         d->absPath = absPath;
0419         return true;
0420     }
0421     return false;
0422 }
0423 
0424 void KUriFilterData::setCheckForExecutables(bool check)
0425 {
0426     d->checkForExecs = check;
0427 }
0428 
0429 void KUriFilterData::setAlternateSearchProviders(const QStringList &providers)
0430 {
0431     d->alternateSearchProviders = providers;
0432 }
0433 
0434 void KUriFilterData::setAlternateDefaultSearchProvider(const QString &provider)
0435 {
0436     d->alternateDefaultSearchProvider = provider;
0437 }
0438 
0439 void KUriFilterData::setDefaultUrlScheme(const QString &scheme)
0440 {
0441     d->defaultUrlScheme = scheme;
0442 }
0443 
0444 void KUriFilterData::setSearchFilteringOptions(SearchFilterOptions options)
0445 {
0446     d->searchFilterOptions = options;
0447 }
0448 
0449 KUriFilterData &KUriFilterData::operator=(const QUrl &url)
0450 {
0451     d->setData(url, url.toString());
0452     return *this;
0453 }
0454 
0455 KUriFilterData &KUriFilterData::operator=(const QString &url)
0456 {
0457     d->setData(QUrl(url), url);
0458     return *this;
0459 }
0460 
0461 /*************************  KUriFilterPlugin ******************************/
0462 
0463 KUriFilterPlugin::KUriFilterPlugin(const QString &name, QObject *parent)
0464     : QObject(parent)
0465     , d(nullptr)
0466 {
0467     setObjectName(name);
0468 }
0469 
0470 // KF6 TODO
0471 // KUriFilterPlugin::~KUriFilterPlugin()
0472 //{
0473 //}
0474 
0475 KCModule *KUriFilterPlugin::configModule(QWidget *, const char *) const
0476 {
0477     return nullptr;
0478 }
0479 
0480 QString KUriFilterPlugin::configName() const
0481 {
0482     return objectName();
0483 }
0484 
0485 void KUriFilterPlugin::setFilteredUri(KUriFilterData &data, const QUrl &uri) const
0486 {
0487     data.d->url = uri.adjusted(QUrl::NormalizePathSegments);
0488     data.d->wasModified = true;
0489     // qDebug() << "Got filtered to:" << uri;
0490 }
0491 
0492 void KUriFilterPlugin::setErrorMsg(KUriFilterData &data, const QString &errmsg) const
0493 {
0494     data.d->errMsg = errmsg;
0495 }
0496 
0497 void KUriFilterPlugin::setUriType(KUriFilterData &data, KUriFilterData::UriTypes type) const
0498 {
0499     data.d->uriType = type;
0500     data.d->wasModified = true;
0501 }
0502 
0503 void KUriFilterPlugin::setArguments(KUriFilterData &data, const QString &args) const
0504 {
0505     data.d->args = args;
0506 }
0507 
0508 void KUriFilterPlugin::setSearchProvider(KUriFilterData &data, const QString &provider, const QString &term, const QChar &separator) const
0509 {
0510     data.d->searchProvider = provider;
0511     data.d->searchTerm = term;
0512     data.d->searchTermSeparator = separator;
0513 }
0514 
0515 void KUriFilterPlugin::setSearchProviders(KUriFilterData &data, const QList<KUriFilterSearchProvider *> &providers) const
0516 {
0517     data.d->searchProviderList.reserve(data.d->searchProviderList.size() + providers.size());
0518     for (KUriFilterSearchProvider *searchProvider : providers) {
0519         data.d->searchProviderList << searchProvider->name();
0520         data.d->searchProviderMap.insert(searchProvider->name(), searchProvider);
0521     }
0522 }
0523 
0524 QString KUriFilterPlugin::iconNameFor(const QUrl &url, KUriFilterData::UriTypes type) const
0525 {
0526     return lookupIconNameFor(url, type);
0527 }
0528 
0529 QHostInfo KUriFilterPlugin::resolveName(const QString &hostname, unsigned long timeout) const
0530 {
0531     return KIO::HostInfo::lookupHost(hostname, timeout);
0532 }
0533 
0534 /*******************************  KUriFilter ******************************/
0535 
0536 class KUriFilterPrivate
0537 {
0538 public:
0539     KUriFilterPrivate()
0540     {
0541     }
0542     ~KUriFilterPrivate()
0543     {
0544         qDeleteAll(pluginList);
0545         pluginList.clear();
0546     }
0547     QVector<KUriFilterPlugin *> pluginList;
0548 };
0549 
0550 class KUriFilterSingleton
0551 {
0552 public:
0553     KUriFilter instance;
0554 };
0555 
0556 Q_GLOBAL_STATIC(KUriFilterSingleton, m_self)
0557 
0558 KUriFilter *KUriFilter::self()
0559 {
0560     return &m_self()->instance;
0561 }
0562 
0563 KUriFilter::KUriFilter()
0564     : d(new KUriFilterPrivate())
0565 {
0566     loadPlugins();
0567 }
0568 
0569 KUriFilter::~KUriFilter() = default;
0570 
0571 bool KUriFilter::filterUri(KUriFilterData &data, const QStringList &filters)
0572 {
0573     bool filtered = false;
0574 
0575     for (KUriFilterPlugin *plugin : std::as_const(d->pluginList)) {
0576         // If no specific filters were requested, iterate through all the plugins.
0577         // Otherwise, only use available filters.
0578         if (filters.isEmpty() || filters.contains(plugin->objectName())) {
0579             if (plugin->filterUri(data)) {
0580                 filtered = true;
0581             }
0582         }
0583     }
0584 
0585     return filtered;
0586 }
0587 
0588 bool KUriFilter::filterUri(QUrl &uri, const QStringList &filters)
0589 {
0590     KUriFilterData data(uri);
0591     bool filtered = filterUri(data, filters);
0592     if (filtered) {
0593         uri = data.uri();
0594     }
0595     return filtered;
0596 }
0597 
0598 bool KUriFilter::filterUri(QString &uri, const QStringList &filters)
0599 {
0600     KUriFilterData data(uri);
0601     bool filtered = filterUri(data, filters);
0602     if (filtered) {
0603         uri = data.uri().toString();
0604     }
0605     return filtered;
0606 }
0607 
0608 QUrl KUriFilter::filteredUri(const QUrl &uri, const QStringList &filters)
0609 {
0610     KUriFilterData data(uri);
0611     filterUri(data, filters);
0612     return data.uri();
0613 }
0614 
0615 QString KUriFilter::filteredUri(const QString &uri, const QStringList &filters)
0616 {
0617     KUriFilterData data(uri);
0618     filterUri(data, filters);
0619     return data.uri().toString();
0620 }
0621 
0622 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(4, 6)
0623 bool KUriFilter::filterSearchUri(KUriFilterData &data)
0624 {
0625     return filterSearchUri(data, (NormalTextFilter | WebShortcutFilter));
0626 }
0627 #endif
0628 
0629 bool KUriFilter::filterSearchUri(KUriFilterData &data, SearchFilterTypes types)
0630 {
0631     QStringList filters;
0632 
0633     if (types & WebShortcutFilter) {
0634         filters << QStringLiteral("kurisearchfilter");
0635     }
0636 
0637     if (types & NormalTextFilter) {
0638         filters << QStringLiteral("kuriikwsfilter");
0639     }
0640 
0641     return filterUri(data, filters);
0642 }
0643 
0644 QStringList KUriFilter::pluginNames() const
0645 {
0646     QStringList res;
0647     res.reserve(d->pluginList.size());
0648     std::transform(d->pluginList.constBegin(), d->pluginList.constEnd(), std::back_inserter(res), [](const KUriFilterPlugin *plugin) {
0649         return plugin->objectName();
0650     });
0651     return res;
0652 }
0653 
0654 void KUriFilter::loadPlugins()
0655 {
0656     QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf" QT_STRINGIFY(QT_VERSION_MAJOR) "/urifilters"));
0657     const QString prefKey = QStringLiteral("X-KDE-InitialPreference");
0658     // Sort the plugins by order of priority
0659     std::sort(plugins.begin(), plugins.end(), [prefKey](const KPluginMetaData &a, const KPluginMetaData &b) {
0660         return a.value(prefKey, 0) > b.value(prefKey, 0);
0661     });
0662 
0663     QStringList pluginNames;
0664     pluginNames.reserve(plugins.count());
0665 
0666     for (const KPluginMetaData &pluginMetaData : std::as_const(plugins)) {
0667         const QString fileName = pluginMetaData.fileName().section(QLatin1Char('/'), -1);
0668         if (!pluginNames.contains(fileName)) {
0669             pluginNames << fileName;
0670             if (auto plugin = KPluginFactory::instantiatePlugin<KUriFilterPlugin>(pluginMetaData).plugin) {
0671                 d->pluginList << plugin;
0672             }
0673         }
0674     }
0675 }
0676 
0677 #include "moc_kurifilter.cpp"