1 /*****************************************************************************
2  * Copyright (C) 2002 Shie Erlich <erlich@users.sourceforge.net>             *
3  * Copyright (C) 2002 Rafi Yanai <yanai@users.sourceforge.net>               *
4  * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org]              *
5  *                                                                           *
6  * This file is part of Krusader [https://krusader.org].                     *
7  *                                                                           *
8  * Krusader is free software: you can redistribute it and/or modify          *
9  * it under the terms of the GNU General Public License as published by      *
10  * the Free Software Foundation, either version 2 of the License, or         *
11  * (at your option) any later version.                                       *
12  *                                                                           *
13  * Krusader is distributed in the hope that it will be useful,               *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
16  * GNU General Public License for more details.                              *
17  *                                                                           *
18  * You should have received a copy of the GNU General Public License         *
19  * along with Krusader.  If not, see [http://www.gnu.org/licenses/].         *
20  *****************************************************************************/
21 
22 #include "krservices.h"
23 
24 // QtCore
25 #include <QDir>
26 #include <QSet>
27 #include <QTextStream>
28 #include <QtGlobal>
29 
30 #include <KConfigCore/KSharedConfig>
31 #include <KIOCore/KProtocolManager>
32 
33 #include "krglobal.h"
34 #include "defaults.h"
35 
36 QMap<QString, QString>* KrServices::slaveMap = 0;
37 QSet<QString> KrServices::krarcArchiveMimetypes = KrServices::generateKrarcArchiveMimetypes();
38 #ifdef KRARC_QUERY_ENABLED
39 QSet<QString> KrServices::isoArchiveMimetypes = QSet<QString>::fromList(KProtocolInfo::archiveMimetypes("iso"));
40 #else
41 QSet<QString> KrServices::isoArchiveMimetypes;
42 #endif
43 
44 QString KrServices::GLOBAL_MESSAGE_PATTERN = "%{time hh:mm:ss.zzz}-%{type} %{category} %{function}@%{line} # %{message}";
45 
generateKrarcArchiveMimetypes()46 QSet<QString> KrServices::generateKrarcArchiveMimetypes()
47 {
48     // Hard-code these proven mimetypes openable by krarc protocol.
49     // They cannot be listed in krarc.protocol itself
50     // because it would baffle other file managers (like Dolphin).
51     QSet<QString> mimes;
52     mimes += QString("application/x-deb");
53     mimes += QString("application/x-debian-package");
54     mimes += QString("application/vnd.debian.binary-package");
55     mimes += QString("application/x-java-archive");
56     mimes += QString("application/x-rpm");
57     mimes += QString("application/x-source-rpm");
58     mimes += QString("application/vnd.oasis.opendocument.chart");
59     mimes += QString("application/vnd.oasis.opendocument.database");
60     mimes += QString("application/vnd.oasis.opendocument.formula");
61     mimes += QString("application/vnd.oasis.opendocument.graphics");
62     mimes += QString("application/vnd.oasis.opendocument.presentation");
63     mimes += QString("application/vnd.oasis.opendocument.spreadsheet");
64     mimes += QString("application/vnd.oasis.opendocument.text");
65     mimes += QString("application/vnd.openxmlformats-officedocument.presentationml.presentation");
66     mimes += QString("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
67     mimes += QString("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
68     mimes += QString("application/x-cbz");
69     mimes += QString("application/x-cbr");
70     mimes += QString("application/epub+zip");
71     mimes += QString("application/x-webarchive");
72     mimes += QString("application/x-plasma");
73     mimes += QString("application/vnd.rar");
74 
75     #ifdef KRARC_QUERY_ENABLED
76     mimes += QSet<QString>::fromList(KProtocolInfo::archiveMimetypes("krarc"));
77     #endif
78 
79     return mimes;
80 }
81 
cmdExist(QString cmdName)82 bool KrServices::cmdExist(QString cmdName)
83 {
84     KConfigGroup group(krConfig, "Dependencies");
85     if (QFile(group.readEntry(cmdName, QString())).exists())
86         return true;
87 
88     return !QStandardPaths::findExecutable(cmdName).isEmpty();
89 }
90 
fullPathName(QString name,QString confName)91 QString KrServices::fullPathName(QString name, QString confName)
92 {
93     QString supposedName;
94 
95     if (confName.isNull())
96         confName = name;
97 
98     KConfigGroup config(krConfig, "Dependencies");
99     if (QFile(supposedName = config.readEntry(confName, QString())).exists())
100         return supposedName;
101 
102     if ((supposedName = QStandardPaths::findExecutable(name)).isEmpty())
103         return "";
104 
105     config.writeEntry(confName, supposedName);
106     return supposedName;
107 }
108 
chooseFullPathName(QStringList names,QString confName)109 QString KrServices::chooseFullPathName(QStringList names, QString confName)
110 {
111     foreach(const QString &name, names) {
112         QString foundTool = KrServices::fullPathName(name, confName);
113         if (! foundTool.isEmpty()) {
114             return foundTool;
115         }
116     }
117 
118     return "";
119 }
120 
isExecutable(const QString & path)121 bool KrServices::isExecutable(const QString &path)
122 {
123     QFileInfo info(path);
124     return info.isFile() && info.isExecutable();
125 }
126 
registeredProtocol(QString mimetype)127 QString KrServices::registeredProtocol(QString mimetype)
128 {
129     if (slaveMap == 0) {
130         slaveMap = new QMap<QString, QString>();
131 
132         KConfigGroup group(krConfig, "Protocols");
133         QStringList protList = group.readEntry("Handled Protocols", QStringList());
134         for (QStringList::Iterator it = protList.begin(); it != protList.end(); ++it) {
135             QStringList mimes = group.readEntry(QString("Mimes For %1").arg(*it), QStringList());
136             for (QStringList::Iterator it2 = mimes.begin(); it2 != mimes.end(); ++it2)
137                 (*slaveMap)[*it2] = *it;
138         }
139     }
140     QString protocol = (*slaveMap)[mimetype];
141     if (protocol.isEmpty()) {
142         if (krarcArchiveMimetypes.contains(mimetype)) {
143             return "krarc";
144         }
145         protocol = KProtocolManager::protocolForArchiveMimetype(mimetype);
146     }
147     return protocol;
148 }
149 
isoSupported(QString mimetype)150 bool KrServices::isoSupported(QString mimetype)
151 {
152     return isoArchiveMimetypes.contains(mimetype);
153 }
154 
clearProtocolCache()155 void KrServices::clearProtocolCache()
156 {
157     if (slaveMap)
158         delete slaveMap;
159     slaveMap = 0;
160 }
161 
fileToStringList(QTextStream * stream,QStringList & target,bool keepEmptyLines)162 bool KrServices::fileToStringList(QTextStream *stream, QStringList& target, bool keepEmptyLines)
163 {
164     if (!stream) return false;
165     QString line;
166     while (!stream->atEnd()) {
167         line = stream->readLine().trimmed();
168         if (keepEmptyLines || !line.isEmpty()) target.append(line);
169     }
170     return true;
171 }
172 
fileToStringList(QFile * file,QStringList & target,bool keepEmptyLines)173 bool KrServices::fileToStringList(QFile *file, QStringList& target, bool keepEmptyLines)
174 {
175     QTextStream stream(file);
176     return fileToStringList(&stream, target, keepEmptyLines);
177 }
178 
quote(QString name)179 QString KrServices::quote(QString name)
180 {
181     if (!name.contains('\''))
182         return '\'' + name + '\'';
183     if (!name.contains('"') && !name.contains('$'))
184         return '\"' + name + '\"';
185     return escape(name);
186 }
187 
quote(const QStringList & names)188 QStringList KrServices::quote(const QStringList& names)
189 {
190     QStringList result;
191     for (int i = 0; i < names.size(); ++i)
192         result.append(quote(names[i]));
193     return result;
194 }
195 
toUrlList(const QStringList & list)196 QList<QUrl> KrServices::toUrlList(const QStringList &list)
197 {
198     QList<QUrl> result;
199     for (QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) {
200         result.append(QUrl::fromUserInput(*it, QDir::currentPath(), QUrl::AssumeLocalFile));
201     }
202     return result;
203 }
204 
toStringList(const QList<QUrl> & list)205 QStringList KrServices::toStringList(const QList<QUrl> &list)
206 {
207     QStringList result;
208     for(QList<QUrl>::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) {
209         result.append(it->toString());
210     }
211     return result;
212 }
213 
214 // Adds one tool to the list in the supportedTools method
supportedTool(QStringList & tools,QString toolType,QStringList names,QString confName)215 void supportedTool(QStringList &tools, QString toolType,
216                    QStringList names, QString confName) {
217     QString foundTool = KrServices::chooseFullPathName(names, confName);
218     if (! foundTool.isEmpty()) {
219         tools.append(toolType);
220         tools.append(foundTool);
221     }
222 }
223 
224 // return a list in the format of TOOLS,PATH. for example
225 // DIFF,kdiff,TERMINAL,konsole,...
226 //
227 // currently supported tools: DIFF, MAIL, RENAME
228 //
229 // to use it: QStringList lst = supportedTools();
230 //            int i = lst.indexOf("DIFF");
231 //            if (i!=-1) pathToDiff=lst[i+1];
supportedTools()232 QStringList KrServices::supportedTools() {
233     QStringList tools;
234 
235     // first, a diff program: kdiff
236     supportedTool(tools, "DIFF",
237                   QStringList() << "kdiff3" << "kompare" << "xxdiff",
238                   "diff utility");
239 
240     // a mailer: kmail or thunderbird
241     supportedTool(tools, "MAIL",
242                   QStringList() << "thunderbird" << "kmail",
243                   "mailer");
244 
245     // rename tool: krename
246     supportedTool(tools, "RENAME",
247                   QStringList() << "krename",
248                   "krename");
249 
250     // checksum utility
251     supportedTool(tools, "MD5",
252                   QStringList() << "md5sum",
253                   "checksum utility");
254 
255     return tools;
256 }
257 
258 
escape(QString name)259 QString KrServices::escape(QString name)
260 {
261     const QString evilstuff = "\\\"'`()[]{}!?;$&<>| \t\r\n";  // stuff that should get escaped
262 
263     for (int i = 0; i < evilstuff.length(); ++i)
264         name.replace(evilstuff[ i ], ('\\' + evilstuff[ i ]));
265 
266     return name;
267 }
268 
escapeFileUrl(QString urlString)269 QString KrServices::escapeFileUrl(QString urlString)
270 {
271     // Avoid that if a path contains a '#' then what follows the '#' be interpreted as the fragment identifier of
272     // 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
273     return urlString.replace('#', "%23").replace('?', "%3F");
274 }
275 
escapeFileUrl(const QUrl & url)276 QUrl KrServices::escapeFileUrl(const QUrl &url)
277 {
278     return QUrl(KrServices::escapeFileUrl(url.toString()));
279 }
280 
urlToLocalPath(const QUrl & url)281 QString KrServices::urlToLocalPath(const QUrl &url)
282 {
283     QUrl fileUrl = QUrl(url);
284     // QUrl::toLocalFile() does not work if the protocol is "file" e.g. when opening an archive
285     fileUrl.setScheme("file");
286     QString path = fileUrl.toLocalFile();
287     REPLACE_DIR_SEP2(path);
288 
289 #ifdef Q_WS_WIN
290     if (path.startsWith(DIR_SEPARATOR)) {
291         int p = 1;
292         while (p < path.length() && path[ p ] == DIR_SEPARATOR_CHAR)
293             p++;
294         /* /C:/Folder */
295         if (p + 2 <= path.length() && path[ p ].isLetter() && path[ p + 1 ] == ':') {
296             path = path.mid(p);
297         }
298     }
299 #endif
300     return path;
301 }
302 
303 static bool s_withDebugMessages;
304 static QtMessageHandler s_defaultMessageHandler;
305 
setGlobalKrMessageHandler(bool withDebugMessages)306 void KrServices::setGlobalKrMessageHandler(bool withDebugMessages)
307 {
308     s_withDebugMessages = withDebugMessages;
309     s_defaultMessageHandler = qInstallMessageHandler(0);
310     qInstallMessageHandler(&krMessageHandler);
311 }
312 
krMessageHandler(QtMsgType type,const QMessageLogContext & context,const QString & msg)313 void KrServices::krMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
314 {
315     // filter debug if not enabled
316     if (type != QtDebugMsg || s_withDebugMessages) {
317         s_defaultMessageHandler(type, context, msg);
318     }
319 }
320