File indexing completed on 2024-03-24 17:26:05

0001 /*
0002     SPDX-FileCopyrightText: 2002 Shie Erlich <erlich@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2002 Rafi Yanai <yanai@users.sourceforge.net>
0004     SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "krservices.h"
0010 
0011 // QtCore
0012 #include <QDir>
0013 #include <QSet>
0014 #include <QTextStream>
0015 #include <QtGlobal>
0016 
0017 #include <KConfigCore/KSharedConfig>
0018 #include <KIOCore/KProtocolManager>
0019 #include <utility>
0020 
0021 #include "defaults.h"
0022 #include "krglobal.h"
0023 
0024 QString KrServices::GLOBAL_MESSAGE_PATTERN = "%{time hh:mm:ss.zzz}-%{type} %{category} %{function}@%{line} # %{message}";
0025 
0026 bool KrServices::cmdExist(const QString &cmdName)
0027 {
0028     // Reminder: If that function is modified, it's important to research if the
0029     // changes must also be applied to `KrServices::fullPathName()`
0030     // and `kio_krarcProtocol::fullPathName()`
0031 
0032     KConfigGroup dependGrp(krConfig, "Dependencies");
0033     QString supposedName = dependGrp.readEntry(cmdName, QString());
0034     if (QFileInfo::exists(supposedName))
0035         return true;
0036 
0037     if ((supposedName = QStandardPaths::findExecutable(cmdName)).isEmpty())
0038         return false;
0039 
0040     // Because an executable file has been found, its path is remembered
0041     // in order to avoid some future searches
0042     dependGrp.writeEntry(cmdName, supposedName);
0043 
0044     return true;
0045 }
0046 
0047 QString KrServices::fullPathName(const QString &name, QString confName)
0048 {
0049     // Reminder: If that function is modified, it's important to research if the
0050     // changes must also be applied to `kio_krarcProtocol::fullPathName()`
0051     // and `KrServices::cmdExist()`
0052 
0053     if (confName.isNull())
0054         confName = name;
0055 
0056     KConfigGroup dependGrp(krConfig, "Dependencies");
0057     QString supposedName = dependGrp.readEntry(confName, QString());
0058     if (QFileInfo::exists(supposedName))
0059         return supposedName;
0060 
0061     if ((supposedName = QStandardPaths::findExecutable(name)).isEmpty())
0062         return QString();
0063 
0064     // Because an executable file has been found, its path is remembered
0065     // in order to avoid some future searches
0066     dependGrp.writeEntry(confName, supposedName);
0067 
0068     return supposedName;
0069 }
0070 
0071 QString KrServices::chooseFullPathName(QStringList names, const QString &confName)
0072 {
0073     foreach (const QString &name, names) {
0074         QString foundTool = KrServices::fullPathName(name, confName);
0075         if (!foundTool.isEmpty()) {
0076             return foundTool;
0077         }
0078     }
0079 
0080     return "";
0081 }
0082 
0083 bool KrServices::isExecutable(const QString &path)
0084 {
0085     QFileInfo info(path);
0086     return info.isFile() && info.isExecutable();
0087 }
0088 
0089 bool KrServices::isoSupported(const QString &mimetype)
0090 {
0091 #ifdef KRARC_QUERY_ENABLED
0092     return KProtocolInfo::archiveMimetypes("iso").contains(mimetype);
0093 #else
0094     return false;
0095 #endif
0096 }
0097 
0098 bool KrServices::fileToStringList(QTextStream *stream, QStringList &target, bool keepEmptyLines)
0099 {
0100     if (!stream)
0101         return false;
0102     QString line;
0103     while (!stream->atEnd()) {
0104         line = stream->readLine().trimmed();
0105         if (keepEmptyLines || !line.isEmpty())
0106             target.append(line);
0107     }
0108     return true;
0109 }
0110 
0111 bool KrServices::fileToStringList(QFile *file, QStringList &target, bool keepEmptyLines)
0112 {
0113     QTextStream stream(file);
0114     return fileToStringList(&stream, target, keepEmptyLines);
0115 }
0116 
0117 QString KrServices::quote(const QString &name)
0118 {
0119     if (!name.contains('\''))
0120         return '\'' + name + '\'';
0121     if (!name.contains('"') && !name.contains('$'))
0122         return '\"' + name + '\"';
0123     return escape(name);
0124 }
0125 
0126 QStringList KrServices::quote(const QStringList &names)
0127 {
0128     QStringList result;
0129     for (int i = 0; i < names.size(); ++i)
0130         result.append(quote(names[i]));
0131     return result;
0132 }
0133 
0134 QList<QUrl> KrServices::toUrlList(const QStringList &list)
0135 {
0136     QList<QUrl> result;
0137     for (QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) {
0138         result.append(QUrl::fromUserInput(*it, QDir::currentPath(), QUrl::AssumeLocalFile));
0139     }
0140     return result;
0141 }
0142 
0143 QStringList KrServices::toStringList(const QList<QUrl> &list)
0144 {
0145     QStringList result;
0146     for (QList<QUrl>::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) {
0147         result.append(it->toString());
0148     }
0149     return result;
0150 }
0151 
0152 // Adds one tool to the list in the supportedTools method
0153 void supportedTool(QStringList &tools, const QString &toolType, QStringList names, QString confName)
0154 {
0155     QString foundTool = KrServices::chooseFullPathName(std::move(names), std::move(confName));
0156     if (!foundTool.isEmpty()) {
0157         tools.append(toolType);
0158         tools.append(foundTool);
0159     }
0160 }
0161 
0162 // return a list in the format of TOOLS,PATH. for example
0163 // DIFF,kdiff,TERMINAL,konsole,...
0164 //
0165 // currently supported tools: DIFF, MAIL, RENAME
0166 //
0167 // to use it: QStringList lst = supportedTools();
0168 //            int i = lst.indexOf("DIFF");
0169 //            if (i!=-1) pathToDiff=lst[i+1];
0170 QStringList KrServices::supportedTools()
0171 {
0172     QStringList tools;
0173 
0174     // first, a diff program: kdiff
0175     supportedTool(tools,
0176                   "DIFF",
0177                   QStringList() << "kdiff3"
0178                                 << "kompare"
0179                                 << "xxdiff",
0180                   "diff utility");
0181 
0182     // a mailer: kmail or thunderbird
0183     supportedTool(tools,
0184                   "MAIL",
0185                   QStringList() << "thunderbird"
0186                                 << "kmail",
0187                   "mailer");
0188 
0189     // rename tool: krename
0190     supportedTool(tools, "RENAME", QStringList() << "krename", "krename");
0191 
0192     // checksum utility
0193     supportedTool(tools, "MD5", QStringList() << "md5sum", "checksum utility");
0194 
0195     return tools;
0196 }
0197 
0198 QString KrServices::escape(QString name)
0199 {
0200     const QString evilstuff = "\\\"'`()[]{}!?;$&<>| \t\r\n"; // stuff that should get escaped
0201 
0202     for (auto i : evilstuff)
0203         name.replace(i, ('\\' + i));
0204 
0205     return name;
0206 }
0207 
0208 QString KrServices::escapeFileUrl(QString urlString)
0209 {
0210     // Avoid that if a path contains a '#' then what follows the '#' be interpreted as the fragment identifier of
0211     // the URL and not a part of the file path; for more information https://bugs.kde.org/show_bug.cgi?id=270150 can be seen
0212     return urlString.replace('#', "%23").replace('?', "%3F");
0213 }
0214 
0215 QUrl KrServices::escapeFileUrl(const QUrl &url)
0216 {
0217     return QUrl(KrServices::escapeFileUrl(url.toString()));
0218 }
0219 
0220 QString KrServices::urlToLocalPath(const QUrl &url)
0221 {
0222     QUrl fileUrl = QUrl(url);
0223     // QUrl::toLocalFile() does not work if the protocol is "file" e.g. when opening an archive
0224     fileUrl.setScheme("file");
0225     QString path = fileUrl.toLocalFile();
0226     REPLACE_DIR_SEP2(path);
0227 
0228 #ifdef Q_OS_WIN
0229     if (path.startsWith(DIR_SEPARATOR)) {
0230         int p = 1;
0231         while (p < path.length() && path[p] == DIR_SEPARATOR_CHAR)
0232             p++;
0233         /* /C:/Folder */
0234         if (p + 2 <= path.length() && path[p].isLetter() && path[p + 1] == ':') {
0235             path = path.mid(p);
0236         }
0237     }
0238 #endif
0239     return path;
0240 }
0241 
0242 static bool s_withDebugMessages;
0243 static QtMessageHandler s_defaultMessageHandler;
0244 
0245 void KrServices::setGlobalKrMessageHandler(bool withDebugMessages)
0246 {
0247     s_withDebugMessages = withDebugMessages;
0248     s_defaultMessageHandler = qInstallMessageHandler(nullptr);
0249     qInstallMessageHandler(&krMessageHandler);
0250 }
0251 
0252 void KrServices::krMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
0253 {
0254     // filter debug if not enabled
0255     if (type != QtDebugMsg || s_withDebugMessages) {
0256         s_defaultMessageHandler(type, context, msg);
0257     }
0258 }