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