diff --git a/src/urifilters/CMakeLists.txt b/src/urifilters/CMakeLists.txt index 63a98204..a3d7eb1b 100644 --- a/src/urifilters/CMakeLists.txt +++ b/src/urifilters/CMakeLists.txt @@ -1,8 +1,4 @@ -# TODO: Remove these -remove_definitions(-DQT_NO_CAST_FROM_ASCII) -remove_definitions(-DQT_NO_CAST_FROM_BYTEARRAY) - add_subdirectory(localdomain) add_subdirectory(ikws) add_subdirectory(shorturi) add_subdirectory(fixhost) diff --git a/src/urifilters/ikws/ikwsopts.cpp b/src/urifilters/ikws/ikwsopts.cpp index 82c5ac50..153fc2e7 100644 --- a/src/urifilters/ikws/ikwsopts.cpp +++ b/src/urifilters/ikws/ikwsopts.cpp @@ -1,453 +1,453 @@ /* * Copyright (c) 2000 Yves Arrouye * Copyright (c) 2001, 2002 Dawit Alemayehu * Copyright (c) 2009 Nick Shaforostoff * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ikwsopts.h" #include "ikwsopts_p.h" #include "kuriikwsfiltereng.h" #include "searchprovider.h" #include "searchproviderdlg.h" #include #include #include #include #include #include #include #include //BEGIN ProvidersModel ProvidersModel::~ProvidersModel() { } QVariant ProvidersModel::headerData(int section, Qt::Orientation orientation, int role ) const { Q_UNUSED(orientation); if (role == Qt::DisplayRole) { switch (section) { case Name: return i18nc("@title:column Name label from web shortcuts column", "Name"); case Shortcuts: return i18nc("@title:column", "Shortcuts"); case Preferred: return i18nc("@title:column", "Preferred"); default: break; } } return QVariant(); } Qt::ItemFlags ProvidersModel::flags(const QModelIndex& index) const { if (!index.isValid()) return Qt::ItemIsEnabled; if (index.column()==Preferred) return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } bool ProvidersModel::setData (const QModelIndex& index, const QVariant& value, int role) { if (role==Qt::CheckStateRole) { if (value.toInt() == Qt::Checked) m_favoriteEngines.insert(m_providers.at(index.row())->desktopEntryName()); else m_favoriteEngines.remove(m_providers.at(index.row())->desktopEntryName()); emit dataModified(); return true; } return false; } QVariant ProvidersModel::data(const QModelIndex& index, int role) const { if (index.isValid()) { if (role == Qt::CheckStateRole && index.column()==Preferred) return (m_favoriteEngines.contains(m_providers.at(index.row())->desktopEntryName()) ? Qt::Checked : Qt::Unchecked); if (role == Qt::DisplayRole) { if (index.column()==Name) return m_providers.at(index.row())->name(); if (index.column()==Shortcuts) return m_providers.at(index.row())->keys().join(QStringLiteral(",")); } if (role == Qt::ToolTipRole || role == Qt::WhatsThisRole) { if (index.column() == Preferred) return xi18nc("@info:tooltip", "Check this box to select the highlighted web shortcut " "as preferred.Preferred web shortcuts are used in " "places where only a few select shortcuts can be shown " "at one time."); } if (role == Qt::UserRole) return index.row();//a nice way to bypass proxymodel } return QVariant(); } void ProvidersModel::setProviders(const QList& providers, const QStringList& favoriteEngines) { m_providers = providers; setFavoriteProviders(favoriteEngines); } void ProvidersModel::setFavoriteProviders(const QStringList& favoriteEngines) { beginResetModel(); m_favoriteEngines = QSet::fromList(favoriteEngines); endResetModel(); } int ProvidersModel::rowCount(const QModelIndex & parent) const { if (parent.isValid()) return 0; return m_providers.size(); } QAbstractListModel* ProvidersModel::createListModel() { ProvidersListModel* pListModel = new ProvidersListModel(m_providers, this); connect(this, SIGNAL(modelAboutToBeReset()), pListModel, SIGNAL(modelAboutToBeReset())); connect(this, SIGNAL(modelReset()), pListModel, SIGNAL(modelReset())); connect(this, SIGNAL(layoutAboutToBeChanged()), pListModel, SIGNAL(modelReset())); connect(this, SIGNAL(layoutChanged()), pListModel, SIGNAL(modelReset())); connect(this, SIGNAL(dataChanged(QModelIndex,QModelIndex)), pListModel, SLOT(emitDataChanged(QModelIndex,QModelIndex))); connect(this, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), pListModel, SLOT(emitRowsAboutToBeInserted(QModelIndex,int,int))); connect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), pListModel, SLOT(emitRowsAboutToBeRemoved(QModelIndex,int,int))); connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), pListModel, SLOT(emitRowsInserted(QModelIndex,int,int))); connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), pListModel, SLOT(emitRowsRemoved(QModelIndex,int,int))); return pListModel; } void ProvidersModel::deleteProvider(SearchProvider* p) { const int row = m_providers.indexOf(p); beginRemoveRows(QModelIndex(), row, row); m_favoriteEngines.remove(m_providers.takeAt(row)->desktopEntryName()); endRemoveRows(); delete p; emit dataModified(); } void ProvidersModel::addProvider(SearchProvider* p) { beginInsertRows(QModelIndex(), m_providers.size(), m_providers.size()); m_providers.append(p); endInsertRows(); emit dataModified(); } void ProvidersModel::changeProvider(SearchProvider* p) { const int row = m_providers.indexOf(p); emit dataChanged(index(row,0),index(row,ColumnCount-1)); emit dataModified(); } QStringList ProvidersModel::favoriteEngines() const { return m_favoriteEngines.toList(); } //END ProvidersModel //BEGIN ProvidersListModel ProvidersListModel::ProvidersListModel(QList& providers, QObject* parent) : QAbstractListModel(parent) , m_providers(providers) {} QVariant ProvidersListModel::data(const QModelIndex& index, int role) const { if (index.isValid()) { if (role==Qt::DisplayRole) { if (index.row() == m_providers.size()) return i18nc("@item:inlistbox No default web shortcut", "None"); return m_providers.at(index.row())->name(); } if (role==ShortNameRole) { if (index.row() == m_providers.size()) return QString(); return m_providers.at(index.row())->desktopEntryName(); } } return QVariant(); } int ProvidersListModel::rowCount (const QModelIndex& parent) const { if (parent.isValid()) return 0; return m_providers.size() + 1; } //END ProvidersListModel static QSortFilterProxyModel* wrapInProxyModel(QAbstractItemModel* model) { QSortFilterProxyModel* proxyModel = new QSortFilterProxyModel(model); proxyModel->setSourceModel(model); proxyModel->setDynamicSortFilter(true); proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); proxyModel->setFilterKeyColumn(-1); return proxyModel; } FilterOptions::FilterOptions(const KAboutData* about, QWidget *parent) : KCModule(about, parent), m_providersModel(new ProvidersModel(this)) { m_dlg.setupUi(this); QSortFilterProxyModel* searchProviderModel = wrapInProxyModel(m_providersModel); m_dlg.lvSearchProviders->setModel(searchProviderModel); m_dlg.cmbDefaultEngine->setModel(wrapInProxyModel(m_providersModel->createListModel())); // Connect all the signals/slots... connect(m_dlg.cbEnableShortcuts, SIGNAL(toggled(bool)), SLOT(changed())); connect(m_dlg.cbEnableShortcuts, SIGNAL(toggled(bool)), SLOT(updateSearchProviderEditingButons())); connect(m_dlg.cbUseSelectedShortcutsOnly, SIGNAL(toggled(bool)), SLOT(changed())); connect(m_providersModel, SIGNAL(dataModified()), SLOT(changed())); connect(m_dlg.cmbDefaultEngine, SIGNAL(currentIndexChanged(int)), SLOT(changed())); connect(m_dlg.cmbDelimiter, SIGNAL(currentIndexChanged(int)), SLOT(changed())); connect(m_dlg.pbNew, SIGNAL(clicked()), SLOT(addSearchProvider())); connect(m_dlg.pbDelete, SIGNAL(clicked()), SLOT(deleteSearchProvider())); connect(m_dlg.pbChange, SIGNAL(clicked()), SLOT(changeSearchProvider())); connect(m_dlg.lvSearchProviders->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), SLOT(updateSearchProviderEditingButons())); connect(m_dlg.lvSearchProviders, SIGNAL(doubleClicked(QModelIndex)),SLOT(changeSearchProvider())); connect(m_dlg.searchLineEdit, SIGNAL(textEdited(QString)), searchProviderModel, SLOT(setFilterFixedString(QString))); } QString FilterOptions::quickHelp() const { return xi18nc("@info:whatsthis", "In this module you can configure the web shortcuts feature. " "Web shortcuts allow you to quickly search or lookup words on " "the Internet. For example, to search for information about the " "KDE project using the Google engine, you simply type gg:KDE " "or google:KDE." "If you select a default search engine, then you can search for " "normal words or phrases by simply typing them into the input widget " "of applications that have built-in support for such a feature, e.g " "Konqueror."); } void FilterOptions::setDefaultEngine(int index) { QSortFilterProxyModel* proxy = qobject_cast(m_dlg.cmbDefaultEngine->model()); if (index == -1) index = proxy->rowCount()-1;//"None" is the last const QModelIndex modelIndex = proxy->mapFromSource(proxy->sourceModel()->index(index,0)); m_dlg.cmbDefaultEngine->setCurrentIndex(modelIndex.row()); m_dlg.cmbDefaultEngine->view()->setCurrentIndex(modelIndex); //TODO: remove this when Qt bug is fixed } void FilterOptions::load() { - KConfig config(KURISearchFilterEngine::self()->name() + QStringLiteral("rc"), KConfig::NoGlobals); + KConfig config(QString::fromUtf8(KURISearchFilterEngine::self()->name()) + QLatin1String("rc"), KConfig::NoGlobals); KConfigGroup group = config.group("General"); const QString defaultSearchEngine = group.readEntry("DefaultWebShortcut"); const QStringList favoriteEngines = group.readEntry("PreferredWebShortcuts", DEFAULT_PREFERRED_SEARCH_PROVIDERS); const QList providers = m_registry.findAll(); int defaultProviderIndex = providers.size(); //default is "None", it is last in the list for (SearchProvider *provider : providers) { if (defaultSearchEngine == provider->desktopEntryName()) defaultProviderIndex = providers.size(); } m_providersModel->setProviders(providers, favoriteEngines); m_dlg.lvSearchProviders->setColumnWidth(0,200); m_dlg.lvSearchProviders->resizeColumnToContents(1); m_dlg.lvSearchProviders->sortByColumn(0,Qt::AscendingOrder); m_dlg.cmbDefaultEngine->model()->sort(0,Qt::AscendingOrder); setDefaultEngine(defaultProviderIndex); m_dlg.cbEnableShortcuts->setChecked(group.readEntry("EnableWebShortcuts", true)); m_dlg.cbUseSelectedShortcutsOnly->setChecked(group.readEntry("UsePreferredWebShortcutsOnly", false)); const QString delimiter = group.readEntry ("KeywordDelimiter", ":"); setDelimiter(delimiter.at(0).toLatin1()); } char FilterOptions::delimiter() { const char delimiters[]={':',' '}; return delimiters[m_dlg.cmbDelimiter->currentIndex()]; } void FilterOptions::setDelimiter (char sep) { m_dlg.cmbDelimiter->setCurrentIndex(sep==' '); } void FilterOptions::save() { - KConfig config(KURISearchFilterEngine::self()->name() + QStringLiteral("rc"), KConfig::NoGlobals ); + KConfig config(QString::fromUtf8(KURISearchFilterEngine::self()->name()) + QLatin1String("rc"), KConfig::NoGlobals ); KConfigGroup group = config.group("General"); group.writeEntry("EnableWebShortcuts", m_dlg.cbEnableShortcuts->isChecked()); group.writeEntry("KeywordDelimiter", QString(QLatin1Char(delimiter()))); group.writeEntry("DefaultWebShortcut", m_dlg.cmbDefaultEngine->view()->currentIndex().data(ProvidersListModel::ShortNameRole)); group.writeEntry("PreferredWebShortcuts", m_providersModel->favoriteEngines()); group.writeEntry("UsePreferredWebShortcutsOnly", m_dlg.cbUseSelectedShortcutsOnly->isChecked()); int changedProviderCount = 0; QList providers = m_providersModel->providers(); - const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kservices5/searchproviders/"; + const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/searchproviders/"); Q_FOREACH(SearchProvider* provider, providers) { if (!provider->isDirty()) continue; changedProviderCount++; - KConfig _service(path + provider->desktopEntryName() + ".desktop", KConfig::SimpleConfig ); + KConfig _service(path + provider->desktopEntryName() + QLatin1String(".desktop"), KConfig::SimpleConfig); KConfigGroup service(&_service, "Desktop Entry"); service.writeEntry("Type", "Service"); service.writeEntry("X-KDE-ServiceTypes", "SearchProvider"); service.writeEntry("Name", provider->name()); service.writeEntry("Query", provider->query()); service.writeEntry("Keys", provider->keys()); service.writeEntry("Charset", provider->charset()); service.writeEntry("Hidden", false); // we might be overwriting a hidden entry } const QStringList servicesDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kservices5/searchproviders/"), QStandardPaths::LocateDirectory); Q_FOREACH(const QString& providerName, m_deletedProviders) { QStringList matches; foreach(const QString& dir, servicesDirs) { - QString current = dir + '/' + providerName + ".desktop"; + QString current = dir + QLatin1Char('/') + providerName + QLatin1String(".desktop"); if(QFile::exists(current)) matches += current; } // Shouldn't happen if (!matches.size()) continue; changedProviderCount++; if (matches.size() == 1 && matches.first().startsWith(path)) { // If only the local copy existed, unlink it // TODO: error handling QFile::remove(matches.first()); continue; } - KConfig _service(path + providerName + ".desktop", KConfig::SimpleConfig ); + KConfig _service(path + providerName + QLatin1String(".desktop"), KConfig::SimpleConfig); KConfigGroup service(&_service, "Desktop Entry"); service.writeEntry("Type", "Service"); service.writeEntry("X-KDE-ServiceTypes", "SearchProvider"); service.writeEntry("Hidden", true); } config.sync(); emit changed(false); // Update filters in running applications... QDBusMessage msg = QDBusMessage::createSignal(QStringLiteral("/"), QStringLiteral("org.kde.KUriFilterPlugin"), QStringLiteral("configure")); QDBusConnection::sessionBus().send(msg); // If the providers changed, tell sycoca to rebuild its database... if (changedProviderCount) KBuildSycocaProgressDialog::rebuildKSycoca(this); } void FilterOptions::defaults() { m_dlg.cbEnableShortcuts->setChecked(true); m_dlg.cbUseSelectedShortcutsOnly->setChecked(false); m_providersModel->setFavoriteProviders(DEFAULT_PREFERRED_SEARCH_PROVIDERS); setDelimiter(':'); setDefaultEngine(-1); } void FilterOptions::addSearchProvider() { QList providers = m_providersModel->providers(); QPointer dlg = new SearchProviderDialog(nullptr, providers, this); if (dlg->exec()) { m_providersModel->addProvider(dlg->provider()); m_providersModel->changeProvider(dlg->provider()); } delete dlg; } void FilterOptions::changeSearchProvider() { QList providers = m_providersModel->providers(); SearchProvider* provider = providers.at(m_dlg.lvSearchProviders->currentIndex().data(Qt::UserRole).toInt()); QPointer dlg = new SearchProviderDialog(provider, providers, this); if (dlg->exec()) m_providersModel->changeProvider(dlg->provider()); delete dlg; } void FilterOptions::deleteSearchProvider() { SearchProvider* provider = m_providersModel->providers().at(m_dlg.lvSearchProviders->currentIndex().data(Qt::UserRole).toInt()); m_deletedProviders.append(provider->desktopEntryName()); m_providersModel->deleteProvider(provider); } void FilterOptions::updateSearchProviderEditingButons() { const bool enable = (m_dlg.cbEnableShortcuts->isChecked() && m_dlg.lvSearchProviders->currentIndex().isValid()); m_dlg.pbChange->setEnabled(enable); m_dlg.pbDelete->setEnabled(enable); } // kate: replace-tabs 1; indent-width 2; diff --git a/src/urifilters/ikws/kuriikwsfiltereng.cpp b/src/urifilters/ikws/kuriikwsfiltereng.cpp index ccb42c61..07b65a72 100644 --- a/src/urifilters/ikws/kuriikwsfiltereng.cpp +++ b/src/urifilters/ikws/kuriikwsfiltereng.cpp @@ -1,449 +1,449 @@ /* This file is part of the KDE project Copyright (C) 2002, 2003 Dawit Alemayehu Copyright (C) 2000 Yves Arrouye Copyright (C) 1999 Simon Hausmann Advanced web shortcuts: Copyright (C) 2001 Andreas Hochsteger This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kuriikwsfiltereng.h" #include "searchprovider.h" #include #include #include #include #include namespace { QLoggingCategory category("org.kde.kurifilter-ikws", QtWarningMsg); } #define PDVAR(n,v) qCDebug(category) << n << " = '" << v << "'" /** * IMPORTANT: If you change anything here, make sure you run the kurifiltertest * regression test (this should be included as part of "make test"). */ KURISearchFilterEngine::KURISearchFilterEngine() { loadConfig(); } KURISearchFilterEngine::~KURISearchFilterEngine() { } SearchProvider* KURISearchFilterEngine::webShortcutQuery(const QString& typedString, QString &searchTerm) const { SearchProvider *provider = nullptr; if (m_bWebShortcutsEnabled) { - const int pos = typedString.indexOf(m_cKeywordDelimiter); + const int pos = typedString.indexOf(QLatin1Char(m_cKeywordDelimiter)); QString key; if ( pos > -1 ) key = typedString.left(pos).toLower(); // #169801 else if ( !typedString.isEmpty() && m_cKeywordDelimiter == ' ') key = typedString; - qCDebug(category) << "m_cKeywordDelimiter=" << QString(QChar(m_cKeywordDelimiter)) << "pos=" << pos << "key=" << key; + qCDebug(category) << "m_cKeywordDelimiter=" << QLatin1Char(m_cKeywordDelimiter) << "pos=" << pos << "key=" << key; if (!key.isEmpty() && !KProtocolInfo::isKnownProtocol(key)) { provider = m_registry.findByKey(key); if (provider) { if (!m_bUseOnlyPreferredWebShortcuts || m_preferredWebShortcuts.contains(provider->desktopEntryName())) { searchTerm = typedString.mid(pos+1); qCDebug(category) << "found provider" << provider->desktopEntryName() << "searchTerm=" << searchTerm; } else { provider = nullptr; } } } } return provider; } SearchProvider* KURISearchFilterEngine::autoWebSearchQuery(const QString& typedString, const QString &defaultShortcut) const { SearchProvider *provider = nullptr; const QString defaultSearchProvider = (m_defaultWebShortcut.isEmpty() ? defaultShortcut : m_defaultWebShortcut); if (m_bWebShortcutsEnabled && !defaultSearchProvider.isEmpty()) { // Make sure we ignore supported protocols, e.g. "smb:", "http:" - const int pos = typedString.indexOf(':'); + const int pos = typedString.indexOf(QLatin1Char(':')); if (pos == -1 || !KProtocolInfo::isKnownProtocol(typedString.left(pos))) { provider = m_registry.findByDesktopName(defaultSearchProvider); } } return provider; } QByteArray KURISearchFilterEngine::name() const { return "kuriikwsfilter"; } char KURISearchFilterEngine::keywordDelimiter() const { return m_cKeywordDelimiter; } QString KURISearchFilterEngine::defaultSearchEngine() const { return m_defaultWebShortcut; } QStringList KURISearchFilterEngine::favoriteEngineList() const { return m_preferredWebShortcuts; } Q_GLOBAL_STATIC(KURISearchFilterEngine, sSelfPtr) KURISearchFilterEngine* KURISearchFilterEngine::self() { return sSelfPtr; } QStringList KURISearchFilterEngine::modifySubstitutionMap(SubstMap& map, const QString& query) const { // Returns the number of query words QString userquery = query; // Do some pre-encoding, before we can start the work: { int start = 0; int pos = 0; QRegExp qsexpr(QStringLiteral("\\\"[^\\\"]*\\\"")); // Temporary substitute spaces in quoted strings (" " -> "%20") // Needed to split user query into StringList correctly. while ((pos = qsexpr.indexIn(userquery, start)) >= 0) { QString s = userquery.mid (pos, qsexpr.matchedLength()); - s.replace (' ', QLatin1String("%20")); + s.replace(QLatin1Char(' '), QLatin1String("%20")); start = pos + s.length(); // Move after last quote userquery = userquery.replace (pos, qsexpr.matchedLength(), s); } } // Split user query between spaces: - QStringList l = userquery.simplified().split(' ', QString::SkipEmptyParts); + QStringList l = userquery.simplified().split(QLatin1Char(' '), QString::SkipEmptyParts); // Back-substitute quoted strings (%20 -> " "): userquery.replace (QLatin1String("%20"), QLatin1String(" ")); l.replaceInStrings(QStringLiteral("%20"), QStringLiteral(" ")); qCDebug(category) << "Generating substitution map:\n"; // Generate substitution map from user query: for (int i=0; i<=l.count(); i++) { int pos = 0; QString v; QString nr = QString::number(i); // Add whole user query (\{0}) to substitution map: if (i==0) v = userquery; // Add partial user query items to substitution map: else v = l[i-1]; // Insert partial queries (referenced by \1 ... \n) to map: map.insert(QString::number(i), v); - PDVAR (" map['" + nr + "']", map[nr]); + PDVAR (QLatin1String(" map['") + nr + QLatin1String("']"), map[nr]); // Insert named references (referenced by \name) to map: - if ((i>0) && (pos = v.indexOf('=')) > 0) + if ((i>0) && (pos = v.indexOf(QLatin1Char('='))) > 0) { QString s = v.mid(pos + 1); QString k = v.left(pos); // Back-substitute references contained in references (e.g. '\refname' substitutes to 'thisquery=\0') s.replace(QLatin1String("%5C"), QLatin1String("\\")); map.insert(k, s); - PDVAR (" map['" + k + "']", map[k]); + PDVAR (QLatin1String(" map['") + k + QLatin1String("']"), map[k]); } } return l; } static QString encodeString(const QString& s, QTextCodec *codec) { // don't encode the space character, we replace it with + after the encoding QByteArray encoded = codec->fromUnicode(s).toPercentEncoding(QByteArrayLiteral(" ")); encoded.replace(' ', '+'); return QString::fromUtf8(encoded); } QString KURISearchFilterEngine::substituteQuery(const QString& url, SubstMap &map, const QString& userquery, QTextCodec *codec) const { QString newurl = url; QStringList ql = modifySubstitutionMap (map, userquery); int count = ql.count(); // Check, if old style '\1' is found and replace it with \{@} (compatibility mode): { int pos = -1; if ((pos = newurl.indexOf(QStringLiteral("\\1"))) >= 0) { qCWarning(category) << "WARNING: Using compatibility mode for newurl='" << newurl << "'. Please replace old style '\\1' with new style '\\{0}' " "in the query definition.\n"; newurl = newurl.replace(pos, 2, QStringLiteral("\\{@}")); } } qCDebug(category) << "Substitute references:\n"; // Substitute references (\{ref1,ref2,...}) with values from user query: { int pos = 0; QRegExp reflist(QStringLiteral("\\\\\\{[^\\}]+\\}")); // Substitute reflists (\{ref1,ref2,...}): while ((pos = reflist.indexIn(newurl)) >= 0) { bool found = false; //bool rest = false; - QString v = QLatin1String(""); + QString v; QString rlstring = newurl.mid(pos + 2, reflist.matchedLength() - 3); PDVAR (" reference list", rlstring); // \{@} gets a special treatment later if (rlstring == QLatin1String("@")) { v = QStringLiteral("\\@"); found = true; } // TODO: strip whitespaces around commas - QStringList rl = rlstring.split(',', QString::SkipEmptyParts); + QStringList rl = rlstring.split(QLatin1Char(','), QString::SkipEmptyParts); int i = 0; while ((i= 0) { - int pos = rlitem.indexOf(QStringLiteral("-")); + int pos = rlitem.indexOf(QLatin1Char('-')); int first = rlitem.leftRef(pos).toInt(); int last = rlitem.rightRef(rlitem.length()-pos-1).toInt(); if (first == 0) first = 1; if (last == 0) last = count; for (int i=first; i<=last; i++) { - v += map[QString::number(i)] + ' '; + v += map[QString::number(i)] + QLatin1Char(' '); // Remove used value from ql (needed for \{@}): ql[i-1] = QLatin1String(""); } v = v.trimmed(); if (!v.isEmpty()) found = true; - PDVAR (" range", QString::number(first) + '-' + QString::number(last) + " => '" + v + '\''); + PDVAR (QLatin1String(" range"), QString::number(first) + QLatin1Char('-') + QString::number(last) + QLatin1String(" => '") + v + QLatin1Char('\'')); v = encodeString(v, codec); } - else if ( rlitem.startsWith('\"') && rlitem.endsWith('\"') ) + else if (rlitem.startsWith(QLatin1Char('\"')) && rlitem.endsWith(QLatin1Char('\"'))) { // Use default string from query definition: found = true; QString s = rlitem.mid(1, rlitem.length() - 2); v = encodeString(s, codec); PDVAR (" default", s); } else if (map.contains(rlitem)) { // Use value from substitution map: found = true; - PDVAR (" map['" + rlitem + "']", map[rlitem]); + PDVAR (QLatin1String(" map['") + rlitem + QLatin1String("']"), map[rlitem]); v = encodeString(map[rlitem], codec); // Remove used value from ql (needed for \{@}): QString c = rlitem.left(1); if (c==QLatin1String("0")) { // It's a numeric reference to '0' for (QStringList::Iterator it = ql.begin(); it!=ql.end(); ++it) (*it) = QLatin1String(""); } else if ((c>=QLatin1String("0")) && (c<=QLatin1String("9"))) // krazy:excludeall=doublequote_chars { // It's a numeric reference > '0' int n = rlitem.toInt(); ql[n-1] = QLatin1String(""); } else { // It's a alphanumeric reference QStringList::Iterator it = ql.begin(); - while ((it != ql.end()) && !it->startsWith(rlitem + '=')) + while ((it != ql.end()) && !it->startsWith(rlitem + QLatin1Char('='))) ++it; if (it != ql.end()) it->clear(); } // Encode '+', otherwise it would be interpreted as space in the resulting url: - v.replace('+', QLatin1String("%2B")); + v.replace(QLatin1Char('+'), QLatin1String("%2B")); } else if (rlitem == QLatin1String("@")) { v = QStringLiteral("\\@"); PDVAR (" v", v); } i++; } newurl.replace(pos, reflist.matchedLength(), v); } // Special handling for \{@}; { PDVAR (" newurl", newurl); // Generate list of unmatched strings: QString v = ql.join(QStringLiteral(" ")).simplified(); PDVAR (" rest", v); v = encodeString(v, codec); // Substitute \{@} with list of unmatched query strings newurl.replace(QLatin1String("\\@"), v); } } return newurl; } QUrl KURISearchFilterEngine::formatResult( const QString& url, const QString& cset1, const QString& cset2, const QString& query, bool isMalformed ) const { SubstMap map; return formatResult (url, cset1, cset2, query, isMalformed, map); } QUrl KURISearchFilterEngine::formatResult( const QString& url, const QString& cset1, const QString& cset2, const QString& userquery, bool /* isMalformed */, SubstMap& map ) const { // Return nothing if userquery is empty and it contains // substitution strings... if (userquery.isEmpty() && url.indexOf(QStringLiteral("\\{")) > 0) return QUrl(); // Debug info of map: if (!map.isEmpty()) { qCDebug(category) << "Got non-empty substitution map:\n"; for(SubstMap::Iterator it = map.begin(); it != map.end(); ++it) - PDVAR (" map['" + it.key() + "']", it.value()); + PDVAR (QLatin1Literal(" map['") + it.key() + QLatin1Literal("']"), it.value()); } // Create a codec for the desired encoding so that we can transcode the user's "url". QString cseta = cset1; if (cseta.isEmpty()) cseta = QStringLiteral("UTF-8"); QTextCodec *csetacodec = QTextCodec::codecForName(cseta.toLatin1()); if (!csetacodec) { cseta = QStringLiteral("UTF-8"); csetacodec = QTextCodec::codecForName(cseta.toLatin1()); } PDVAR ("user query", userquery); PDVAR ("query definition", url); // Add charset indicator for the query to substitution map: map.insert(QStringLiteral("ikw_charset"), cseta); // Add charset indicator for the fallback query to substitution map: QString csetb = cset2; if (csetb.isEmpty()) csetb = QStringLiteral("UTF-8"); map.insert(QStringLiteral("wsc_charset"), csetb); QString newurl = substituteQuery (url, map, userquery, csetacodec); PDVAR ("substituted query", newurl); return QUrl(newurl, QUrl::StrictMode); } void KURISearchFilterEngine::loadConfig() { qCDebug(category) << "Keywords Engine: Loading config..."; // Load the config. - KConfig config( name() + QStringLiteral("rc"), KConfig::NoGlobals ); + KConfig config(QString::fromUtf8(name()) + QLatin1String("rc"), KConfig::NoGlobals); KConfigGroup group = config.group( "General" ); m_cKeywordDelimiter = QString(group.readEntry("KeywordDelimiter", ":")).at(0).toLatin1(); m_bWebShortcutsEnabled = group.readEntry("EnableWebShortcuts", true); m_defaultWebShortcut = group.readEntry("DefaultWebShortcut"); m_bUseOnlyPreferredWebShortcuts = group.readEntry("UsePreferredWebShortcutsOnly", false); QStringList defaultPreferredShortcuts; if (!group.hasKey("PreferredWebShortcuts")) defaultPreferredShortcuts = DEFAULT_PREFERRED_SEARCH_PROVIDERS; m_preferredWebShortcuts = group.readEntry("PreferredWebShortcuts", defaultPreferredShortcuts); // Use either a white space or a : as the keyword delimiter... if (strchr (" :", m_cKeywordDelimiter) == nullptr) m_cKeywordDelimiter = ':'; qCDebug(category) << "Web Shortcuts Enabled: " << m_bWebShortcutsEnabled; qCDebug(category) << "Default Shortcut: " << m_defaultWebShortcut; qCDebug(category) << "Keyword Delimiter: " << m_cKeywordDelimiter; } SearchProviderRegistry * KURISearchFilterEngine::registry() { return &m_registry; } diff --git a/src/urifilters/ikws/searchprovider.cpp b/src/urifilters/ikws/searchprovider.cpp index 9232054e..fd8f43be 100644 --- a/src/urifilters/ikws/searchprovider.cpp +++ b/src/urifilters/ikws/searchprovider.cpp @@ -1,128 +1,128 @@ /* * Copyright (c) 2000 Malte Starostik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "searchprovider.h" #include #include #include #include // KIO::iconNameForUrl #include #include #include #include SearchProvider::SearchProvider(const QString &servicePath) : m_dirty(false) { setDesktopEntryName(QFileInfo(servicePath).baseName()); KDesktopFile parser(servicePath); setName(parser.readName()); KConfigGroup group(parser.desktopGroup()); setKeys(group.readEntry(QStringLiteral("Keys"), QStringList())); m_query = group.readEntry(QStringLiteral("Query")); m_charset = group.readEntry(QStringLiteral("Charset")); } SearchProvider::~SearchProvider() { } void SearchProvider::setName(const QString &name) { if (KUriFilterSearchProvider::name() == name) return; KUriFilterSearchProvider::setName(name); } void SearchProvider::setQuery(const QString &query) { if (m_query == query) return; m_query = query; } void SearchProvider::setKeys(const QStringList &keys) { if (KUriFilterSearchProvider::keys() == keys) return; KUriFilterSearchProvider::setKeys(keys); QString name = desktopEntryName(); if (!name.isEmpty()) return; // New provider. Set the desktopEntryName. // Take the longest search shortcut as filename, // if such a file already exists, append a number and increase it // until the name is unique Q_FOREACH(const QString& key, keys) { if (key.length() > name.length()) name = key.toLower(); } - const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kservices5/searchproviders/"; + const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/searchproviders/"); bool firstRun = true; while (true) { QString check(name); if (!firstRun) check += KRandom::randomString(4); - const QString located = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kservices5/searchproviders/") + check + ".desktop"); + const QString located = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kservices5/searchproviders/") + check + QLatin1String(".desktop")); if (located.isEmpty()) { name = check; break; } else if (located.startsWith(path)) { // If it's a deleted (hidden) entry, overwrite it if (KService(located).isDeleted()) break; } firstRun = false; } setDesktopEntryName(name); } void SearchProvider::setCharset(const QString &charset) { if (m_charset == charset) return; m_charset = charset; } QString SearchProvider::iconName() const { return KIO::iconNameForUrl(QUrl(m_query)); } void SearchProvider::setDirty(bool dirty) { m_dirty = dirty; } diff --git a/src/urifilters/ikws/searchproviderdlg.cpp b/src/urifilters/ikws/searchproviderdlg.cpp index 6abd0e6d..57de6c32 100644 --- a/src/urifilters/ikws/searchproviderdlg.cpp +++ b/src/urifilters/ikws/searchproviderdlg.cpp @@ -1,175 +1,175 @@ /* * Copyright (c) 2000 Malte Starostik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "searchproviderdlg.h" #include "searchprovider.h" #include #include #include #include #include #include SearchProviderDialog::SearchProviderDialog(SearchProvider *provider, QList &providers, QWidget *parent) : QDialog( parent ) , m_provider(provider) { setModal(true); m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, this); connect(m_buttons, SIGNAL(accepted()), SLOT(accept())); connect(m_buttons, SIGNAL(rejected()), SLOT(reject())); QWidget* mainWidget = new QWidget(this); m_dlg.setupUi(mainWidget); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(mainWidget); layout->addWidget(m_buttons); m_dlg.leQuery->setMinimumWidth(m_dlg.leQuery->fontMetrics().averageCharWidth() * 50); connect(m_dlg.leName, SIGNAL(textChanged(QString)), SLOT(slotChanged())); connect(m_dlg.leQuery, SIGNAL(textChanged(QString)), SLOT(slotChanged())); connect(m_dlg.leShortcut, SIGNAL(textChanged(QString)), SLOT(slotChanged())); connect(m_dlg.leShortcut, SIGNAL(textChanged(QString)), SLOT(shortcutsChanged(QString))); connect(m_dlg.pbPaste, SIGNAL(clicked()), SLOT(pastePlaceholder())); // Data init m_providers = providers; QStringList charsets = KCharsets::charsets()->availableEncodingNames(); charsets.prepend(i18nc("@item:inlistbox The default character set", "Default")); m_dlg.cbCharset->addItems(charsets); if (m_provider) { setWindowTitle(i18n("Modify Web Shortcut")); m_dlg.leName->setText(m_provider->name()); m_dlg.leQuery->setText(m_provider->query()); m_dlg.leShortcut->setText(m_provider->keys().join(QStringLiteral(","))); m_dlg.cbCharset->setCurrentIndex(m_provider->charset().isEmpty() ? 0 : charsets.indexOf(m_provider->charset())); m_dlg.leName->setEnabled(false); m_dlg.leQuery->setFocus(); } else { setWindowTitle(i18n("New Web Shortcut")); m_dlg.leName->setFocus(); //If the clipboard contains a url copy it to the query lineedit const QClipboard *clipboard = QApplication::clipboard(); const QString url = clipboard->text(); if (!QUrl(url).host().isEmpty()) m_dlg.leQuery->setText(url); m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false); } } void SearchProviderDialog::slotChanged() { m_buttons->button(QDialogButtonBox::Ok)->setEnabled(!(m_dlg.leName->text().isEmpty() || m_dlg.leShortcut->text().isEmpty() || m_dlg.leQuery->text().isEmpty())); } // Check if the user wants to assign shorthands that are already assigned to // another search provider. Invoked on every change to the shortcuts field. void SearchProviderDialog::shortcutsChanged(const QString& newShorthands) { // Convert all spaces to commas. A shorthand should be a single word. // Assume that the user wanted to enter an alternative shorthand and hit // space instead of the comma key. Save cursor position beforehand because // setText() will reset it to the end, which is not what we want when // backspacing something in the middle. int savedCursorPosition = m_dlg.leShortcut->cursorPosition(); - QString normalizedShorthands = QString(newShorthands).replace(' ', ','); + QString normalizedShorthands = QString(newShorthands).replace(QLatin1Char(' '), QLatin1Char(',')); m_dlg.leShortcut->setText(normalizedShorthands); m_dlg.leShortcut->setCursorPosition(savedCursorPosition); QHash contenders; - QSet shorthands = normalizedShorthands.split(',').toSet(); + QSet shorthands = normalizedShorthands.split(QLatin1Char(',')).toSet(); // Look at each shorthand the user entered and wade through the search // provider list in search of a conflicting shorthand. Do not continue // search after finding one, because shorthands should be assigned only // once. Act like data inconsistencies regarding this don't exist (should // probably be handled on load). Q_FOREACH (const QString &shorthand, shorthands) { Q_FOREACH (const SearchProvider* provider, m_providers) { if (provider != m_provider && provider->keys().contains(shorthand)) { contenders.insert(shorthand, provider); break; } } } if (!contenders.isEmpty()) { if (contenders.size() == 1) { m_dlg.noteLabel->setText(i18n("The shortcut \"%1\" is already assigned to \"%2\". Please choose a different one.", contenders.keys().at(0), contenders.values().at(0)->name())); } else { QStringList contenderList; QHash::const_iterator i = contenders.constBegin(); while (i != contenders.constEnd()) { contenderList.append(i18nc("- web short cut (e.g. gg): what it refers to (e.g. Google)", "- %1: \"%2\"", i.key(), i.value()->name())); ++i; } m_dlg.noteLabel->setText(i18n("The following shortcuts are already assigned. Please choose different ones.\n%1", contenderList.join(QLatin1Char('\n')))); } m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false); } else { m_dlg.noteLabel->clear(); } } void SearchProviderDialog::accept() { if ((m_dlg.leQuery->text().indexOf(QStringLiteral("\\{")) == -1) && KMessageBox::warningContinueCancel(nullptr, i18n("The Shortcut URL does not contain a \\{...} placeholder for the user query.\n" "This means that the same page is always going to be visited, " "regardless of the text typed in with the shortcut."), QString(), KGuiItem(i18n("Keep It"))) == KMessageBox::Cancel) { return; } if (!m_provider) m_provider = new SearchProvider; const QString name = m_dlg.leName->text().trimmed(); const QString query = m_dlg.leQuery->text().trimmed(); - QStringList keys = m_dlg.leShortcut->text().trimmed().toLower().split(',', QString::SkipEmptyParts); + QStringList keys = m_dlg.leShortcut->text().trimmed().toLower().split(QLatin1Char(','), QString::SkipEmptyParts); keys.removeDuplicates();// #169801. Remove duplicates... const QString charset = (m_dlg.cbCharset->currentIndex() ? m_dlg.cbCharset->currentText().trimmed() : QString()); m_provider->setDirty((name != m_provider->name() || query != m_provider->query() || keys != m_provider->keys() || charset != m_provider->charset())); m_provider->setName(name); m_provider->setQuery(query); m_provider->setKeys(keys); m_provider->setCharset(charset); QDialog::accept(); } void SearchProviderDialog::pastePlaceholder() { m_dlg.leQuery->insert(QStringLiteral("\\{@}")); m_dlg.leQuery->setFocus(); } diff --git a/src/urifilters/ikws/searchproviderregistry.cpp b/src/urifilters/ikws/searchproviderregistry.cpp index a58b0831..bf62edf3 100644 --- a/src/urifilters/ikws/searchproviderregistry.cpp +++ b/src/urifilters/ikws/searchproviderregistry.cpp @@ -1,86 +1,86 @@ /* * This file is part of the KDE project * Copyright 2017 David Faure * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "searchproviderregistry.h" #include "searchprovider.h" #include #include #include SearchProviderRegistry::SearchProviderRegistry() { reload(); } SearchProviderRegistry::~SearchProviderRegistry() { qDeleteAll(m_searchProviders); } QStringList SearchProviderRegistry::directories() const { const QString testDir = QFile::decodeName(qgetenv("KIO_SEARCHPROVIDERS_DIR")); // for unittests if (!testDir.isEmpty()) return { testDir }; return QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kservices5/searchproviders/"), QStandardPaths::LocateDirectory); } void SearchProviderRegistry::reload() { m_searchProvidersByKey.clear(); m_searchProvidersByDesktopName.clear(); qDeleteAll(m_searchProviders); m_searchProviders.clear(); const QStringList servicesDirs = directories(); for (const QString &dirPath : servicesDirs) { QDir dir(dirPath); const auto files = dir.entryList({QStringLiteral("*.desktop")}, QDir::Files); for (const QString &file : files) { if (!m_searchProvidersByDesktopName.contains(file)) { const QString filePath = dir.path() + QLatin1Char('/') + file; auto *provider = new SearchProvider(filePath); m_searchProvidersByDesktopName.insert(file, provider); m_searchProviders.append(provider); const auto keys = provider->keys(); for (const QString &key : keys) { m_searchProvidersByKey.insert(key, provider); } } } } } QList SearchProviderRegistry::findAll() { return m_searchProviders; } SearchProvider* SearchProviderRegistry::findByKey(const QString& key) const { return m_searchProvidersByKey.value(key); } SearchProvider* SearchProviderRegistry::findByDesktopName(const QString &name) const { - return m_searchProvidersByDesktopName.value(name + ".desktop"); + return m_searchProvidersByDesktopName.value(name + QLatin1String(".desktop")); } diff --git a/src/urifilters/shorturi/kshorturifilter.cpp b/src/urifilters/shorturi/kshorturifilter.cpp index 7d4cf087..a4c46b7f 100644 --- a/src/urifilters/shorturi/kshorturifilter.cpp +++ b/src/urifilters/shorturi/kshorturifilter.cpp @@ -1,568 +1,567 @@ /* -*- c-basic-offset: 2 -*- kshorturifilter.h This file is part of the KDE project Copyright (C) 2000 Dawit Alemayehu Copyright (C) 2000 Malte Starostik This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kshorturifilter.h" #include "../../pathhelpers_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { QLoggingCategory category("org.kde.kurifilter-shorturi", QtWarningMsg); } /** * IMPORTANT: If you change anything here, make sure you run the kurifiltertest * regression test (this should be included as part of "make test"). * * If you add anything, make sure to extend kurifiltertest to make sure it is * covered. */ typedef QMap EntryMap; static QRegularExpression sEnvVarExp (QStringLiteral("\\$[a-zA-Z_][a-zA-Z0-9_]*")); static bool isPotentialShortURL(const QString& cmd) { // Host names and IPv4 address... if (cmd.contains(QLatin1Char('.'))) { return true; } // IPv6 Address... if (cmd.startsWith(QLatin1Char('[')) && cmd.contains(QLatin1Char(':'))) { return true; } return false; } static QString removeArgs( const QString& _cmd ) { QString cmd( _cmd ); - if( cmd[0] != '\'' && cmd[0] != '"' ) + if( cmd[0] != QLatin1Char('\'') && cmd[0] != QLatin1Char('"')) { // Remove command-line options (look for first non-escaped space) int spacePos = 0; do { - spacePos = cmd.indexOf( ' ', spacePos+1 ); - } while ( spacePos > 1 && cmd[spacePos - 1] == '\\' ); + spacePos = cmd.indexOf(QLatin1Char(' '), spacePos+1 ); + } while (spacePos > 1 && cmd[spacePos - 1] == QLatin1Char('\\')); if( spacePos > 0 ) { cmd = cmd.left( spacePos ); qCDebug(category) << "spacePos=" << spacePos << " returning " << cmd; } } return cmd; } static bool isKnownProtocol(const QString &protocol) { if (KProtocolInfo::isKnownProtocol(protocol) || protocol == QLatin1String("mailto")) { return true; } const KService::Ptr service = KMimeTypeTrader::self()->preferredService(QLatin1String("x-scheme-handler/") + protocol); return service; } KShortUriFilter::KShortUriFilter( QObject *parent, const QVariantList & /*args*/ ) :KUriFilterPlugin( QStringLiteral("kshorturifilter"), parent ) { QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/"), QStringLiteral("org.kde.KUriFilterPlugin"), QStringLiteral("configure"), this, SLOT(configure())); configure(); } bool KShortUriFilter::filterUri( KUriFilterData& data ) const { /* * Here is a description of how the shortURI deals with the supplied * data. First it expands any environment variable settings and then * deals with special shortURI cases. These special cases are the "smb:" * URL scheme which is very specific to KDE, "#" and "##" which are * shortcuts for man:/ and info:/ protocols respectively. It then handles * local files. Then it checks to see if the URL is valid and one that is * supported by KDE's IO system. If all the above checks fails, it simply * lookups the URL in the user-defined list and returns without filtering * if it is not found. TODO: the user-defined table is currently only manually * hackable and is missing a config dialog. */ //QUrl url = data.uri(); QString cmd = data.typedString(); int firstNonSlash = 0; - while (firstNonSlash < cmd.length() && (cmd.at(firstNonSlash) == '/')) { + while (firstNonSlash < cmd.length() && (cmd.at(firstNonSlash) == QLatin1Char('/'))) { firstNonSlash++; } if (firstNonSlash > 1) { cmd = cmd.mid(firstNonSlash - 1); } // Replicate what KUrl(cmd) did in KDE4. This could later be folded into the checks further down... QUrl url; if (QDir::isAbsolutePath(cmd)) { url = QUrl::fromLocalFile(cmd); } else { url.setUrl(cmd); } // WORKAROUND: Allow the use of '@' in the username component of a URL since // other browsers such as firefox in their infinite wisdom allow such blatant // violations of RFC 3986. BR# 69326/118413. if (cmd.count(QLatin1Char('@')) > 1) { const int lastIndex = cmd.lastIndexOf(QLatin1Char('@')); // Percent encode all but the last '@'. - QString encodedCmd = QUrl::toPercentEncoding(cmd.left(lastIndex), ":/"); + QString encodedCmd = QString::fromUtf8(QUrl::toPercentEncoding(cmd.left(lastIndex), QByteArrayLiteral(":/"))); encodedCmd += cmd.midRef(lastIndex); cmd = encodedCmd; url.setUrl(encodedCmd); } const bool isMalformed = !url.isValid(); QString protocol = url.scheme(); qCDebug(category) << cmd; // Fix misparsing of "foo:80", QUrl thinks "foo" is the protocol and "80" is the path. // However, be careful not to do that for valid hostless URLs, e.g. file:///foo! if (!protocol.isEmpty() && url.host().isEmpty() && !url.path().isEmpty() - && cmd.contains(':') && !isKnownProtocol(protocol)) { + && cmd.contains(QLatin1Char(':')) && !isKnownProtocol(protocol)) { protocol.clear(); } qCDebug(category) << "url=" << url << "cmd=" << cmd << "isMalformed=" << isMalformed; // TODO: Make this a bit more intelligent for Minicli! There // is no need to make comparisons if the supplied data is a local // executable and only the argument part, if any, changed! (Dawit) // You mean caching the last filtering, to try and reuse it, to save stat()s? (David) const QString starthere_proto = QStringLiteral("start-here:"); if (cmd.indexOf(starthere_proto) == 0 ) { setFilteredUri( data, QUrl(QStringLiteral("system:/")) ); setUriType( data, KUriFilterData::LocalDir ); return true; } // Handle MAN & INFO pages shortcuts... const QString man_proto = QStringLiteral("man:"); const QString info_proto = QStringLiteral("info:"); - if( cmd[0] == '#' || + if( cmd[0] == QLatin1Char('#') || cmd.indexOf( man_proto ) == 0 || cmd.indexOf( info_proto ) == 0 ) { if( cmd.leftRef(2) == QLatin1String("##") ) cmd = QStringLiteral("info:/") + cmd.mid(2); - else if ( cmd[0] == '#' ) + else if ( cmd[0] == QLatin1Char('#') ) cmd = QStringLiteral("man:/") + cmd.mid(1); else if ((cmd==info_proto) || (cmd==man_proto)) - cmd+='/'; + cmd += QLatin1Char('/'); setFilteredUri( data, QUrl( cmd )); setUriType( data, KUriFilterData::Help ); return true; } // Detect UNC style (aka windows SMB) URLs if ( cmd.startsWith( QLatin1String( "\\\\") ) ) { // make sure path is unix style - cmd.replace('\\', '/'); + cmd.replace(QLatin1Char('\\'), QLatin1Char('/')); cmd.prepend( QLatin1String( "smb:" ) ); setFilteredUri( data, QUrl( cmd )); setUriType( data, KUriFilterData::NetProtocol ); return true; } bool expanded = false; // Expanding shortcut to HOME URL... QString path; QString ref; QString query; QString nameFilter; if (QDir::isRelativePath(cmd) && QUrl(cmd).isRelative()) { path = cmd; qCDebug(category) << "path=cmd=" << path; } else { if (url.isLocalFile()) { qCDebug(category) << "hasRef=" << url.hasFragment(); // Split path from ref/query // but not for "/tmp/a#b", if "a#b" is an existing file, // or for "/tmp/a?b" (#58990) if( ( url.hasFragment() || !url.query().isEmpty() ) && !url.path().endsWith(QLatin1Char('/')) ) // /tmp/?foo is a namefilter, not a query { path = url.path(); ref = url.fragment(); qCDebug(category) << "isLocalFile set path to" << path << "and ref to" << ref; query = url.query(); if (path.isEmpty() && !url.host().isEmpty()) - path = '/'; + path = QStringLiteral("/"); } else { if (cmd.startsWith(QLatin1String("file://"))) { path = cmd.mid(strlen("file://")); } else { path = cmd; } qCDebug(category) << "(2) path=cmd=" << path; } } } - if( path[0] == '~' ) + if( path[0] == QLatin1Char('~')) { - int slashPos = path.indexOf('/'); + int slashPos = path.indexOf(QLatin1Char('/')); if( slashPos == -1 ) slashPos = path.length(); if( slashPos == 1 ) // ~/ { path.replace ( 0, 1, QDir::homePath() ); } else // ~username/ { const QString userName (path.mid( 1, slashPos-1 )); KUser user (userName); if( user.isValid() && !user.homeDir().isEmpty()) { path.replace (0, slashPos, user.homeDir()); } else { if (user.isValid()) { setErrorMsg(data, i18n("%1 does not have a home folder.", userName)); } else { setErrorMsg(data, i18n("There is no user called %1.", userName)); } setUriType( data, KUriFilterData::Error ); // Always return true for error conditions so // that other filters will not be invoked !! return true; } } expanded = true; } - else if ( path[0] == '$' ) { + else if ( path[0] == QLatin1Char('$') ) { // Environment variable expansion. auto match = sEnvVarExp.match(path); if ( match.hasMatch() ) { QByteArray exp = qgetenv( path.mid( 1, match.capturedLength() - 1 ).toLocal8Bit().data() ); if (!exp.isEmpty()) { path.replace( 0, match.capturedLength(), QFile::decodeName(exp) ); expanded = true; } } } - if ( expanded || cmd.startsWith( '/' ) ) + if ( expanded || cmd.startsWith(QLatin1Char('/')) ) { // Look for #ref again, after $ and ~ expansion (testcase: $QTDIR/doc/html/functions.html#s) // Can't use QUrl here, setPath would escape it... - const int pos = path.indexOf('#'); + const int pos = path.indexOf(QLatin1Char('#')); if ( pos > -1 ) { const QString newPath = path.left( pos ); if ( QFile::exists( newPath ) ) { ref = path.mid( pos + 1 ); path = newPath; qCDebug(category) << "Extracted ref: path=" << path << " ref=" << ref; } } } bool isLocalFullPath = QDir::isAbsolutePath(path); // Checking for local resource match... // Determine if "uri" is an absolute path to a local resource OR // A local resource with a supplied absolute path in KUriFilterData const QString abs_path = data.absolutePath(); const bool canBeAbsolute = (protocol.isEmpty() && !abs_path.isEmpty()); - const bool canBeLocalAbsolute = (canBeAbsolute && abs_path[0] =='/' && !isMalformed); + const bool canBeLocalAbsolute = (canBeAbsolute && abs_path[0] == QLatin1Char('/') && !isMalformed); bool exists = false; /*qCDebug(category) << "abs_path=" << abs_path << "protocol=" << protocol << "canBeAbsolute=" << canBeAbsolute << "canBeLocalAbsolute=" << canBeLocalAbsolute << "isLocalFullPath=" << isLocalFullPath;*/ QT_STATBUF buff; if ( canBeLocalAbsolute ) { QString abs = QDir::cleanPath( abs_path ); // combine absolute path (abs_path) and relative path (cmd) into abs_path int len = path.length(); - if( (len==1 && path[0]=='.') || (len==2 && path[0]=='.' && path[1]=='.') ) - path += '/'; + if( (len==1 && path[0]==QLatin1Char('.')) || (len==2 && path[0]==QLatin1Char('.') && path[1]==QLatin1Char('.')) ) + path += QLatin1Char('/'); qCDebug(category) << "adding " << abs << " and " << path; - abs = QDir::cleanPath(abs + '/' + path); + abs = QDir::cleanPath(abs + QLatin1Char('/') + path); qCDebug(category) << "checking whether " << abs << " exists."; // Check if it exists - if(QT_STAT(QFile::encodeName(abs), &buff) == 0) { + if(QT_STAT(QFile::encodeName(abs).constData(), &buff) == 0) { path = abs; // yes -> store as the new cmd exists = true; isLocalFullPath = true; } } if (isLocalFullPath && !exists && !isMalformed) { - exists = QT_STAT(QFile::encodeName(path), &buff) == 0; + exists = QT_STAT(QFile::encodeName(path).constData(), &buff) == 0; if ( !exists ) { // Support for name filter (/foo/*.txt), see also KonqMainWindow::detectNameFilter // If the app using this filter doesn't support it, well, it'll simply error out itself - int lastSlash = path.lastIndexOf( '/' ); - if ( lastSlash > -1 && path.indexOf( ' ', lastSlash ) == -1 ) // no space after last slash, otherwise it's more likely command-line arguments + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + if ( lastSlash > -1 && path.indexOf(QLatin1Char(' '), lastSlash ) == -1 ) // no space after last slash, otherwise it's more likely command-line arguments { QString fileName = path.mid( lastSlash + 1 ); QString testPath = path.left(lastSlash); - if ((fileName.indexOf('*') != -1 || fileName.indexOf('[') != -1 || fileName.indexOf( '?' ) != -1) - && QT_STAT(QFile::encodeName(testPath), &buff) == 0) { + if ((fileName.indexOf(QLatin1Char('*')) != -1 || fileName.indexOf(QLatin1Char('[')) != -1 || fileName.indexOf(QLatin1Char('?')) != -1) + && QT_STAT(QFile::encodeName(testPath).constData(), &buff) == 0) { nameFilter = fileName; qCDebug(category) << "Setting nameFilter to" << nameFilter << "and path to" << testPath; path = testPath; exists = true; } } } } qCDebug(category) << "path =" << path << " isLocalFullPath=" << isLocalFullPath << " exists=" << exists << " url=" << url; if( exists ) { QUrl u = QUrl::fromLocalFile(path); qCDebug(category) << "ref=" << ref << "query=" << query; u.setFragment(ref); u.setQuery(query); if (!KUrlAuthorized::authorizeUrlAction( QStringLiteral("open"), QUrl(), u)) { // No authorization, we pretend it's a file will get // an access denied error later on. setFilteredUri( data, u ); setUriType( data, KUriFilterData::LocalFile ); return true; } // Can be abs path to file or directory, or to executable with args bool isDir = ((buff.st_mode & QT_STAT_MASK) == QT_STAT_DIR); if( !isDir && access ( QFile::encodeName(path).data(), X_OK) == 0 ) { qCDebug(category) << "Abs path to EXECUTABLE"; setFilteredUri( data, u ); setUriType( data, KUriFilterData::Executable ); return true; } // Open "uri" as file:/xxx if it is a non-executable local resource. if( isDir || (( buff.st_mode & QT_STAT_MASK ) == QT_STAT_REG) ) { qCDebug(category) << "Abs path as local file or directory"; if ( !nameFilter.isEmpty() ) u.setPath(concatPaths(u.path(), nameFilter)); setFilteredUri( data, u ); setUriType( data, ( isDir ) ? KUriFilterData::LocalDir : KUriFilterData::LocalFile ); return true; } // Should we return LOCAL_FILE for non-regular files too? qCDebug(category) << "File found, but not a regular file nor dir... socket?"; } if( data.checkForExecutables()) { // Let us deal with possible relative URLs to see // if it is executable under the user's $PATH variable. // We try hard to avoid parsing any possible command // line arguments or options that might have been supplied. QString exe = removeArgs( cmd ); qCDebug(category) << "findExe with" << exe; if (!QStandardPaths::findExecutable( exe ).isNull() ) { qCDebug(category) << "EXECUTABLE exe=" << exe; setFilteredUri( data, QUrl::fromLocalFile( exe )); // check if we have command line arguments if( exe != cmd ) setArguments(data, cmd.right(cmd.length() - exe.length())); setUriType( data, KUriFilterData::Executable ); return true; } } // Process URLs of known and supported protocols so we don't have // to resort to the pattern matching scheme below which can possibly // slow things down... if ( !isMalformed && !isLocalFullPath && !protocol.isEmpty() ) { qCDebug(category) << "looking for protocol" << protocol; if (isKnownProtocol(protocol)) { setFilteredUri( data, url ); if ( protocol == QLatin1String("man") || protocol == QLatin1String("help") ) setUriType( data, KUriFilterData::Help ); else setUriType( data, KUriFilterData::NetProtocol ); return true; } } // Short url matches - if ( !cmd.contains( ' ' ) ) - { + if (!cmd.contains(QLatin1Char(' '))) { // Okay this is the code that allows users to supply custom matches for // specific URLs using Qt's regexp class. This is hard-coded for now. // TODO: Make configurable at some point... Q_FOREACH(const URLHint& hint, m_urlHints) { qCDebug(category) << "testing regexp for" << hint.prepend; if (hint.regexp.indexIn(cmd) == 0) { const QString cmdStr = hint.prepend + cmd; QUrl url(cmdStr); qCDebug(category) << "match - prepending" << hint.prepend << "->" << cmdStr << "->" << url; setFilteredUri( data, url ); setUriType( data, hint.type ); return true; } } // No protocol and not malformed means a valid short URL such as kde.org or // user@192.168.0.1. However, it might also be valid only because it lacks // the scheme component, e.g. www.kde,org (illegal ',' before 'org'). The // check below properly deciphers the difference between the two and sends // back the proper result. if (protocol.isEmpty() && isPotentialShortURL(cmd)) { QString urlStr = data.defaultUrlScheme(); if (urlStr.isEmpty()) urlStr = m_strDefaultUrlScheme; const int index = urlStr.indexOf(QLatin1Char(':')); if (index == -1 || !isKnownProtocol(urlStr.left(index))) urlStr += QStringLiteral("://"); urlStr += cmd; QUrl url (urlStr); if (url.isValid()) { setFilteredUri(data, url); setUriType(data, KUriFilterData::NetProtocol); } else if (isKnownProtocol(url.scheme())) { setFilteredUri(data, data.uri()); setUriType(data, KUriFilterData::Error); } return true; } } // If we previously determined that the URL might be a file, // and if it doesn't exist... we'll pretend it exists. // This allows to use it for completion purposes. // (If you change this logic again, look at the commit that was testing // for KUrlAuthorized::authorizeUrlAction("open")) if( isLocalFullPath && !exists ) { QUrl u = QUrl::fromLocalFile(path); u.setFragment(ref); setFilteredUri(data, u); setUriType(data, KUriFilterData::LocalFile); return true; } // If we reach this point, we cannot filter this thing so simply return false // so that other filters, if present, can take a crack at it. return false; } KCModule* KShortUriFilter::configModule( QWidget*, const char* ) const { return nullptr; //new KShortUriOptions( parent, name ); } QString KShortUriFilter::configName() const { // return i18n("&ShortURLs"); we don't have a configModule so no need for a configName that confuses translators return KUriFilterPlugin::configName(); } void KShortUriFilter::configure() { KConfig config( objectName() + QStringLiteral( "rc"), KConfig::NoGlobals ); KConfigGroup cg( config.group("") ); m_strDefaultUrlScheme = cg.readEntry( "DefaultProtocol", QStringLiteral("http://") ); const EntryMap patterns = config.entryMap( QStringLiteral("Pattern") ); const EntryMap protocols = config.entryMap( QStringLiteral("Protocol") ); KConfigGroup typeGroup(&config, "Type"); for( EntryMap::ConstIterator it = patterns.begin(); it != patterns.end(); ++it ) { QString protocol = protocols[it.key()]; if (!protocol.isEmpty()) { int type = typeGroup.readEntry(it.key(), -1); if (type > -1 && type <= KUriFilterData::Unknown) m_urlHints.append( URLHint(it.value(), protocol, static_cast(type) ) ); else m_urlHints.append( URLHint(it.value(), protocol) ); } } } K_PLUGIN_FACTORY_WITH_JSON(KShortUriFilterFactory, "kshorturifilter.json", registerPlugin();) #include "kshorturifilter.moc"