File indexing completed on 2024-05-12 17:22:24

0001 /*
0002     SPDX-FileCopyrightText: 2003 Rafi Yanai <krusader@users.sf.net>
0003     SPDX-FileCopyrightText: 2003 Shie Erlich <krusader@users.sf.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 "krarc.h"
0010 #include "../../app/defaults.h"
0011 
0012 // QtCore
0013 #include <QByteArray>
0014 #include <QCoreApplication>
0015 #include <QDebug>
0016 #include <QDir>
0017 #include <QFile>
0018 #include <QFileInfo>
0019 #include <QMimeDatabase>
0020 #include <QMimeType>
0021 #include <QRegExp>
0022 #include <QTemporaryFile>
0023 #include <QTextCodec>
0024 #include <qplatformdefs.h>
0025 
0026 #include <KArchive/KTar>
0027 #include <KCoreAddons/KProcess>
0028 #include <KI18n/KLocalizedString>
0029 #include <KIO/Job>
0030 #include <KIOCore/KFileItem>
0031 
0032 #include <kio_version.h>
0033 
0034 #include "../../app/compat.h"
0035 #include <errno.h>
0036 
0037 #define MAX_IPC_SIZE (1024 * 32)
0038 #define TRIES_WITH_PASSWORDS 3
0039 
0040 // Pseudo plugin class to embed meta data
0041 class KIOPluginForMetaData : public QObject
0042 {
0043     Q_OBJECT
0044 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0045     Q_PLUGIN_METADATA(IID "org.kde.kio.worker.krarc" FILE "krarc.json")
0046 #else
0047     Q_PLUGIN_METADATA(IID "org.kde.kio.slave.krarc" FILE "krarc.json")
0048 #endif
0049 };
0050 
0051 using namespace KIO;
0052 extern "C" {
0053 
0054 #ifdef KRARC_ENABLED
0055 /* This codec is for being able to handle files which encoding differs from the current locale.
0056  *
0057  * Unfortunately QProcess requires QString parameters for arguments which are encoded to Local8Bit
0058  * If we want to use unzip with ISO-8852-2 when the current locale is UTF-8, it will cause problems.
0059  *
0060  * Workaround:
0061  *  1. encode the QString to QByteArray ( according to the selected remote encoding, ISO-8852-2 )
0062  *  2. encode QByteArray to QString again
0063  *     unicode 0xE000-0xF7FF is for private use
0064  *     the byte array is mapped to 0xE000-0xE0FF unicodes
0065  *  3. KrArcCodec maps 0xE000-0xE0FF to 0x0000-0x00FF, while calls the default encoding routine
0066  *     for other unicodes.
0067  */
0068 
0069 class KrArcCodec : public QTextCodec
0070 {
0071 public:
0072     KrArcCodec(QTextCodec *codec)
0073         : originalCodec(codec)
0074     {
0075     }
0076     ~KrArcCodec() override = default;
0077 
0078     QByteArray name() const override
0079     {
0080         return originalCodec->name();
0081     }
0082     QList<QByteArray> aliases() const override
0083     {
0084         return originalCodec->aliases();
0085     }
0086     int mibEnum() const override
0087     {
0088         return originalCodec->mibEnum();
0089     }
0090 
0091 protected:
0092     QString convertToUnicode(const char *in, int length, ConverterState *state) const override
0093     {
0094         return originalCodec->toUnicode(in, length, state);
0095     }
0096     QByteArray convertFromUnicode(const QChar *in, int length, ConverterState *state) const override
0097     {
0098         // the QByteArray is embedded into the unicode charset (QProcess hell)
0099         QByteArray result;
0100         for (int i = 0; i != length; i++) {
0101             if (((in[i].unicode()) & 0xFF00) == 0xE000) // map 0xE000-0xE0FF to 0x0000-0x00FF
0102                 result.append((char)(in[i].unicode() & 0xFF));
0103             else
0104                 result.append(originalCodec->fromUnicode(in + i, 1, state));
0105         }
0106         return result;
0107     }
0108 
0109 private:
0110     QTextCodec *originalCodec;
0111 } *krArcCodec;
0112 
0113 #define SET_KRCODEC                                                                                                                                            \
0114     QTextCodec *origCodec = QTextCodec::codecForLocale();                                                                                                      \
0115     QTextCodec::setCodecForLocale(krArcCodec);
0116 #define RESET_KRCODEC QTextCodec::setCodecForLocale(origCodec);
0117 
0118 #endif // KRARC_ENABLED
0119 
0120 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0121 class DummyWorker : public KIO::WorkerBase
0122 #else
0123 class DummySlave : public KIO::SlaveBase
0124 #endif
0125 {
0126 public:
0127 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0128     DummyWorker(const QByteArray &pool_socket, const QByteArray &app_socket)
0129         : WorkerBase("kio_krarc", pool_socket, app_socket){
0130 #else
0131     DummySlave(const QByteArray &pool_socket, const QByteArray &app_socket)
0132         : SlaveBase("kio_krarc", pool_socket, app_socket)
0133     {
0134         error((int)ERR_SLAVE_DEFINED, QString("krarc is disabled."));
0135 #endif
0136 
0137         }
0138 };
0139 
0140 int Q_DECL_EXPORT kdemain(int argc, char **argv)
0141 {
0142     if (argc != 4) {
0143         qWarning() << "Usage: kio_krarc  protocol domain-socket1 domain-socket2" << QT_ENDL;
0144         exit(-1);
0145     }
0146 
0147     // At least, that fixes the empty name in the warning that says:  Please fix the "" KIO slave
0148     // There is more information in https://bugs.kde.org/show_bug.cgi?id=384653
0149     QCoreApplication app(argc, argv);
0150     app.setApplicationName(QStringLiteral("kio_krarc"));
0151 
0152 #ifdef KRARC_ENABLED
0153 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0154     kio_krarcProtocol worker(argv[2], argv[3]);
0155 #else
0156     kio_krarcProtocol slave(argv[2], argv[3]);
0157 #endif
0158 
0159 #else
0160 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0161     DummyWorker worker(argv[2], argv[3]);
0162 #else
0163     DummySlave slave(argv[2], argv[3]);
0164 #endif
0165 
0166 #endif
0167 
0168 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0169     worker.dispatchLoop();
0170 #else
0171     slave.dispatchLoop();
0172 #endif
0173 
0174     return 0;
0175 }
0176 
0177 } // extern "C"
0178 
0179 #ifdef KRARC_ENABLED
0180 kio_krarcProtocol::kio_krarcProtocol(const QByteArray &pool_socket, const QByteArray &app_socket)
0181 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0182     : WorkerBase("kio_krarc", pool_socket, app_socket)
0183     , archiveChanged(true)
0184     , arcFile(nullptr)
0185     , extArcReady(false)
0186     ,
0187 #else
0188     : SlaveBase("kio_krarc", pool_socket, app_socket)
0189     , archiveChanged(true)
0190     , arcFile(nullptr)
0191     , extArcReady(false)
0192     ,
0193 #endif
0194     password(QString())
0195     , codec(nullptr)
0196 {
0197     KRFUNC;
0198     KConfigGroup group(&krConf, "General");
0199     QString tmpDirPath = group.readEntry("Temp Directory", _TempDirectory);
0200     QDir tmpDir(tmpDirPath);
0201     if (!tmpDir.exists()) {
0202         for (int i = 1; i != -1; i = tmpDirPath.indexOf('/', i + 1))
0203             QDir().mkdir(tmpDirPath.left(i));
0204         QDir().mkdir(tmpDirPath);
0205     }
0206 
0207     arcTempDir = tmpDirPath + DIR_SEPARATOR;
0208     QString dirName = "krArc" + QDateTime::currentDateTime().toString(Qt::ISODate);
0209     dirName.replace(QRegExp(":"), "_");
0210     tmpDir.mkdir(dirName);
0211     arcTempDir = arcTempDir + dirName + DIR_SEPARATOR;
0212 
0213     krArcCodec = new KrArcCodec(QTextCodec::codecForLocale());
0214 }
0215 
0216 /* ---------------------------------------------------------------------------------- */
0217 kio_krarcProtocol::~kio_krarcProtocol()
0218 {
0219     KRFUNC;
0220     // delete the temp directory
0221     KProcess proc;
0222     proc << fullPathName("rm") << "-rf" << arcTempDir;
0223     proc.start();
0224     proc.waitForFinished();
0225 }
0226 
0227 /* ---------------------------------------------------------------------------------- */
0228 
0229 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0230 KIO::WorkerResult kio_krarcProtocol::checkWriteSupport()
0231 #else
0232 bool kio_krarcProtocol::checkWriteSupport()
0233 #endif
0234 {
0235     KRFUNC;
0236     krConf.reparseConfiguration();
0237     if (KConfigGroup(&krConf, "kio_krarc").readEntry("EnableWrite", false))
0238 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0239         return WorkerResult::pass();
0240 
0241     return WorkerResult::fail(ERR_UNSUPPORTED_ACTION,
0242                               i18n("krarc: write support is disabled.\n"
0243                                    "You can enable it on the 'Archives' page in Konfigurator."));
0244 }
0245 #else
0246         return true;
0247     else {
0248         error(ERR_UNSUPPORTED_ACTION,
0249               i18n("krarc: write support is disabled.\n"
0250                    "You can enable it on the 'Archives' page in Konfigurator."));
0251         return false;
0252     }
0253 }
0254 #endif
0255 
0256 void kio_krarcProtocol::receivedData(KProcess *, QByteArray &d)
0257 {
0258     KRFUNC;
0259     const QByteArray &buf(d);
0260     data(buf);
0261     processedSize(d.length());
0262     decompressedLen += d.length();
0263 }
0264 
0265 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0266 KIO::WorkerResult kio_krarcProtocol::mkdir(const QUrl &url, int permissions)
0267 #else
0268 void kio_krarcProtocol::mkdir(const QUrl &url, int permissions)
0269 #endif
0270 {
0271     KRFUNC;
0272     const QString path = getPath(url);
0273     KRDEBUG(path);
0274 
0275 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0276     const auto writeSupportResult = checkWriteSupport();
0277     if (!writeSupportResult.success())
0278         return writeSupportResult;
0279 #else
0280     if (!checkWriteSupport())
0281         return;
0282 #endif
0283 
0284         // In case of KIO::mkpath call there is a mkdir call for every path element.
0285         // Therefore the path all the way up to our archive needs to be checked for existence
0286         // and reported as success.
0287 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0288     if (QDir().exists(path)) {
0289         return WorkerResult::pass();
0290     }
0291 
0292     const auto setArcFileResult = setArcFile(url);
0293     if (!setArcFileResult.success()) {
0294         return setArcFileResult;
0295     }
0296 
0297     if (newArchiveURL && !initDirDict(url)) {
0298         return WorkerResult::fail(ERR_CANNOT_ENTER_DIRECTORY, path);
0299     }
0300 
0301     if (putCmd.isEmpty()) {
0302         return WorkerResult::fail(ERR_UNSUPPORTED_ACTION, i18n("Creating folders is not supported with %1 archives", arcType));
0303     }
0304 #else
0305     if (QDir().exists(path)) {
0306         finished();
0307         return;
0308     }
0309 
0310     if (!setArcFile(url)) {
0311         error(ERR_CANNOT_ENTER_DIRECTORY, path);
0312         return;
0313     }
0314 
0315     if (newArchiveURL && !initDirDict(url)) {
0316         error(ERR_CANNOT_ENTER_DIRECTORY, path);
0317         return;
0318     }
0319 
0320     if (putCmd.isEmpty()) {
0321         error(ERR_UNSUPPORTED_ACTION, i18n("Creating folders is not supported with %1 archives", arcType));
0322         return;
0323     }
0324 #endif
0325 
0326     const QString arcFilePath = getPath(arcFile->url());
0327 
0328     if (arcType == "arj" || arcType == "lha") {
0329         QString arcDir = path.mid(arcFilePath.length());
0330         if (arcDir.right(1) != DIR_SEPARATOR)
0331             arcDir = arcDir + DIR_SEPARATOR;
0332 
0333         if (dirDict.find(arcDir) == dirDict.end())
0334             addNewDir(arcDir);
0335 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0336         return WorkerResult::pass();
0337 #else
0338         finished();
0339         return;
0340 #endif
0341     }
0342 
0343     QString arcDir = findArcDirectory(url);
0344     QString tempDir = arcDir.mid(1) + path.mid(path.lastIndexOf(DIR_SEPARATOR) + 1);
0345     if (tempDir.right(1) != DIR_SEPARATOR)
0346         tempDir = tempDir + DIR_SEPARATOR;
0347 
0348     if (permissions == -1)
0349         permissions = 0777; // set default permissions
0350 
0351     QByteArray arcTempDirEnc = arcTempDir.toLocal8Bit();
0352     for (int i = 0; i < tempDir.length() && i >= 0; i = tempDir.indexOf(DIR_SEPARATOR, i + 1)) {
0353         QByteArray newDirs = encodeString(tempDir.left(i));
0354         newDirs.prepend(arcTempDirEnc);
0355         QT_MKDIR(newDirs, permissions);
0356     }
0357 
0358     if (tempDir.endsWith(DIR_SEPARATOR))
0359         tempDir.truncate(tempDir.length() - 1);
0360 
0361     // pack the directory
0362     KrLinecountingProcess proc;
0363     proc << putCmd << arcFilePath << localeEncodedString(tempDir);
0364     infoMessage(i18n("Creating %1...", url.fileName()));
0365     QDir::setCurrent(arcTempDir);
0366 
0367     SET_KRCODEC
0368     proc.start();
0369     RESET_KRCODEC
0370 
0371     proc.waitForFinished();
0372 
0373     // delete the temp directory
0374     QDir().rmdir(arcTempDir);
0375 
0376     if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) {
0377 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0378         return WorkerResult::fail(ERR_CANNOT_WRITE, path + "\n\n" + proc.getErrorMsg());
0379 #else
0380         error(ERR_CANNOT_WRITE, path + "\n\n" + proc.getErrorMsg());
0381         return;
0382 #endif
0383     }
0384 
0385     //  force a refresh of archive information
0386     initDirDict(url, true);
0387 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0388     return WorkerResult::pass();
0389 #else
0390     finished();
0391 #endif
0392 }
0393 
0394 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0395 KIO::WorkerResult kio_krarcProtocol::put(const QUrl &url, int permissions, KIO::JobFlags flags)
0396 #else
0397 void kio_krarcProtocol::put(const QUrl &url, int permissions, KIO::JobFlags flags)
0398 #endif
0399 {
0400     KRFUNC;
0401     KRDEBUG(getPath(url));
0402 
0403 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0404     const auto writeSupportResult = checkWriteSupport();
0405     if (!writeSupportResult.success())
0406         return writeSupportResult;
0407 #else
0408     if (!checkWriteSupport())
0409         return;
0410 #endif
0411 
0412     bool overwrite = !!(flags & KIO::Overwrite);
0413     bool resume = !!(flags & KIO::Resume);
0414 
0415 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0416     const auto setArcFileResult = setArcFile(url);
0417     if (!setArcFileResult.success()) {
0418         return setArcFileResult;
0419     }
0420     if (newArchiveURL && !initDirDict(url)) {
0421         return WorkerResult::fail(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
0422     }
0423 
0424     if (putCmd.isEmpty()) {
0425         return WorkerResult::fail(ERR_UNSUPPORTED_ACTION, i18n("Writing to %1 archives is not supported", arcType));
0426     }
0427     if (!overwrite && findFileEntry(url)) {
0428         return WorkerResult::fail(ERR_FILE_ALREADY_EXIST, getPath(url));
0429     }
0430 #else
0431     if (!setArcFile(url)) {
0432         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
0433         return;
0434     }
0435     if (newArchiveURL && !initDirDict(url)) {
0436         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
0437         return;
0438     }
0439 
0440     if (putCmd.isEmpty()) {
0441         error(ERR_UNSUPPORTED_ACTION, i18n("Writing to %1 archives is not supported", arcType));
0442         return;
0443     }
0444     if (!overwrite && findFileEntry(url)) {
0445         error(ERR_FILE_ALREADY_EXIST, getPath(url));
0446         return;
0447     }
0448 #endif
0449 
0450     QString arcDir = findArcDirectory(url);
0451     if (arcDir.isEmpty())
0452         KRDEBUG("arcDir is empty.");
0453 
0454     QString tempFile = arcDir.mid(1) + getPath(url).mid(getPath(url).lastIndexOf(DIR_SEPARATOR) + 1);
0455     QString tempDir = arcDir.mid(1);
0456     if (tempDir.right(1) != DIR_SEPARATOR)
0457         tempDir = tempDir + DIR_SEPARATOR;
0458 
0459     if (permissions == -1)
0460         permissions = 0777; // set default permissions
0461 
0462     QByteArray arcTempDirEnc = arcTempDir.toLocal8Bit();
0463     for (int i = 0; i < tempDir.length() && i >= 0; i = tempDir.indexOf(DIR_SEPARATOR, i + 1)) {
0464         QByteArray newDirs = encodeString(tempDir.left(i));
0465         newDirs.prepend(arcTempDirEnc);
0466         QT_MKDIR(newDirs, 0755);
0467     }
0468 
0469     int fd;
0470     if (resume) {
0471         QByteArray ba = encodeString(tempFile);
0472         ba.prepend(arcTempDirEnc);
0473         fd = QT_OPEN(ba, O_RDWR); // append if resuming
0474         QT_LSEEK(fd, 0, SEEK_END); // Seek to end
0475     } else {
0476         // WABA: Make sure that we keep writing permissions ourselves,
0477         // otherwise we can be in for a surprise on NFS.
0478         mode_t initialMode;
0479         if (permissions != -1)
0480             initialMode = permissions | S_IWUSR | S_IRUSR;
0481         else
0482             initialMode = 0666;
0483 
0484         QByteArray ba = encodeString(tempFile);
0485         ba.prepend(arcTempDirEnc);
0486         fd = QT_OPEN(ba, O_CREAT | O_TRUNC | O_WRONLY, initialMode);
0487     }
0488 
0489     QByteArray buffer;
0490     int readResult;
0491     bool isIncomplete = false;
0492     do {
0493         dataReq();
0494         readResult = readData(buffer);
0495         auto bytesWritten = ::write(fd, buffer.data(), buffer.size());
0496         if (bytesWritten < buffer.size()) {
0497             isIncomplete = true;
0498             break;
0499         }
0500     } while (readResult > 0);
0501     ::close(fd);
0502 
0503     if (isIncomplete) {
0504 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0505         return WorkerResult::fail(ERR_CANNOT_WRITE, getPath(url));
0506 #else
0507         error(ERR_CANNOT_WRITE, getPath(url));
0508         return;
0509 #endif
0510     }
0511 
0512     // pack the file
0513     KrLinecountingProcess proc;
0514     proc << putCmd << getPath(arcFile->url()) << localeEncodedString(tempFile);
0515     infoMessage(i18n("Packing %1...", url.fileName()));
0516     QDir::setCurrent(arcTempDir);
0517 
0518     SET_KRCODEC
0519     proc.start();
0520     RESET_KRCODEC
0521 
0522     proc.waitForFinished();
0523 
0524     // remove the files
0525     QDir().rmdir(arcTempDir);
0526 
0527     if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) {
0528 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0529         return WorkerResult::fail(ERR_CANNOT_WRITE, getPath(url) + "\n\n" + proc.getErrorMsg());
0530 #else
0531         error(ERR_CANNOT_WRITE, getPath(url) + "\n\n" + proc.getErrorMsg());
0532         return;
0533 #endif
0534     }
0535     //  force a refresh of archive information
0536     initDirDict(url, true);
0537 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0538     return WorkerResult::pass();
0539 #else
0540     finished();
0541 #endif
0542 }
0543 
0544 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0545 KIO::WorkerResult kio_krarcProtocol::get(const QUrl &url)
0546 #else
0547 void kio_krarcProtocol::get(const QUrl &url)
0548 #endif
0549 {
0550     KRFUNC;
0551 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0552     return get(url, TRIES_WITH_PASSWORDS);
0553 #else
0554     get(url, TRIES_WITH_PASSWORDS);
0555 #endif
0556 }
0557 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0558 KIO::WorkerResult kio_krarcProtocol::get(const QUrl &url, int tries)
0559 #else
0560 void kio_krarcProtocol::get(const QUrl &url, int tries)
0561 #endif
0562 {
0563     KRFUNC;
0564     KRDEBUG(getPath(url));
0565     bool decompressToFile = false;
0566 
0567 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0568     const auto setArcFileResult = setArcFile(url);
0569     if (!setArcFileResult.success()) {
0570         return setArcFileResult;
0571     }
0572     if (newArchiveURL && !initDirDict(url)) {
0573         return WorkerResult::fail(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
0574     }
0575 
0576     if (getCmd.isEmpty()) {
0577         return WorkerResult::fail(ERR_UNSUPPORTED_ACTION, i18n("Retrieving data from %1 archives is not supported", arcType));
0578     }
0579     UDSEntry *entry = findFileEntry(url);
0580     if (!entry) {
0581         return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, getPath(url));
0582     }
0583     if (KFileItem(*entry, url).isDir()) {
0584         return WorkerResult::fail(KIO::ERR_IS_DIRECTORY, getPath(url));
0585     }
0586 #else
0587     if (!setArcFile(url)) {
0588         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
0589         return;
0590     }
0591     if (newArchiveURL && !initDirDict(url)) {
0592         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
0593         return;
0594     }
0595 
0596     if (getCmd.isEmpty()) {
0597         error(ERR_UNSUPPORTED_ACTION, i18n("Retrieving data from %1 archives is not supported", arcType));
0598         return;
0599     }
0600     UDSEntry *entry = findFileEntry(url);
0601     if (!entry) {
0602         error(KIO::ERR_DOES_NOT_EXIST, getPath(url));
0603         return;
0604     }
0605     if (KFileItem(*entry, url).isDir()) {
0606         error(KIO::ERR_IS_DIRECTORY, getPath(url));
0607         return;
0608     }
0609 #endif
0610 
0611     KIO::filesize_t expectedSize = KFileItem(*entry, url).size();
0612     // for RPM files extract the cpio file first
0613     if (!extArcReady && arcType == "rpm") {
0614         KrLinecountingProcess cpio;
0615         cpio << "rpm2cpio" << getPath(arcFile->url(), QUrl::StripTrailingSlash);
0616         cpio.setStandardOutputFile(arcTempDir + "contents.cpio");
0617 
0618         cpio.start();
0619         cpio.waitForFinished();
0620 
0621         if (cpio.exitStatus() != QProcess::NormalExit || !checkStatus(cpio.exitCode())) {
0622 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0623             return WorkerResult::fail(ERR_CANNOT_READ, getPath(url) + "\n\n" + cpio.getErrorMsg());
0624 #else
0625             error(ERR_CANNOT_READ, getPath(url) + "\n\n" + cpio.getErrorMsg());
0626             return;
0627 #endif
0628         }
0629         extArcReady = true;
0630     }
0631     // for DEB files extract the tar file first
0632     if (!extArcReady && arcType == "deb") {
0633         KrLinecountingProcess dpkg;
0634         dpkg << cmd << "--fsys-tarfile" << getPath(arcFile->url(), QUrl::StripTrailingSlash);
0635         dpkg.setStandardOutputFile(arcTempDir + "contents.cpio");
0636 
0637         dpkg.start();
0638         dpkg.waitForFinished();
0639 
0640         if (dpkg.exitStatus() != QProcess::NormalExit || !checkStatus(dpkg.exitCode())) {
0641 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0642             return WorkerResult::fail(ERR_CANNOT_READ, getPath(url) + "\n\n" + dpkg.getErrorMsg());
0643 #else
0644             error(ERR_CANNOT_READ, getPath(url) + "\n\n" + dpkg.getErrorMsg());
0645             return;
0646 #endif
0647         }
0648         extArcReady = true;
0649     }
0650 
0651     // Use the external unpacker to unpack the file
0652     QString file = getPath(url).mid(getPath(arcFile->url()).length() + 1);
0653     KrLinecountingProcess proc;
0654     if (extArcReady) {
0655         proc << getCmd << arcTempDir + "contents.cpio" << '*' + localeEncodedString(file);
0656     } else if (arcType == "arj" || arcType == "ace" || arcType == "7z") {
0657         proc << getCmd << getPath(arcFile->url(), QUrl::StripTrailingSlash) << localeEncodedString(file);
0658         if (arcType == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!!
0659             proc.setStandardInputFile("/dev/ptmx");
0660         file = url.fileName();
0661         decompressToFile = true;
0662     } else {
0663         decompressedLen = 0;
0664         // Determine the mimetype of the file to be retrieved, and emit it.
0665         // This is mandatory in all slaves (for KRun/BrowserRun to work).
0666         QMimeDatabase db;
0667         QMimeType mt = db.mimeTypeForFile(arcTempDir + file);
0668         if (mt.isValid())
0669             mimeType(mt.name());
0670 
0671         QString escapedFilename = file;
0672         if (arcType == "zip") // left bracket needs to be escaped
0673             escapedFilename.replace('[', "[[]");
0674         proc << getCmd << getPath(arcFile->url());
0675         if (arcType != "gzip" && arcType != "bzip2" && arcType != "lzma" && arcType != "xz")
0676             proc << localeEncodedString(escapedFilename);
0677         connect(&proc, &KrLinecountingProcess::newOutputData, this, &kio_krarcProtocol::receivedData);
0678         proc.setMerge(false);
0679     }
0680     infoMessage(i18n("Unpacking %1...", url.fileName()));
0681     // change the working directory to our arcTempDir
0682     QDir::setCurrent(arcTempDir);
0683 
0684     SET_KRCODEC
0685     proc.setTextModeEnabled(false);
0686     proc.start();
0687     RESET_KRCODEC
0688 
0689     // Wait until the external unpacker has finished
0690     if (!proc.waitForFinished(-1)) {
0691 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0692         return WorkerResult::fail(KIO::ERR_SLAVE_DEFINED,
0693                                   i18n("An error has happened, related to the external program '%1'. "
0694                                        "The error message is: '%2'.",
0695                                        cmd,
0696                                        proc.errorString()));
0697 #else
0698         error(KIO::ERR_SLAVE_DEFINED,
0699               i18n("An error has happened, related to the external program '%1'. "
0700                    "The error message is: '%2'.",
0701                    cmd,
0702                    proc.errorString()));
0703         return;
0704 #endif
0705     }
0706 
0707     if (!extArcReady && !decompressToFile) {
0708         if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())
0709             || (arcType != "bzip2" && arcType != "lzma" && arcType != "xz" && expectedSize != decompressedLen)) {
0710             if (encrypted && tries) {
0711                 invalidatePassword();
0712 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0713                 return get(url, tries - 1);
0714             }
0715             return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, getPath(url) + "\n\n" + proc.getErrorMsg());
0716 #else
0717                 get(url, tries - 1);
0718                 return;
0719             }
0720             error(KIO::ERR_ACCESS_DENIED, getPath(url) + "\n\n" + proc.getErrorMsg());
0721             return;
0722 #endif
0723         }
0724     } else {
0725         if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode()) || !QFileInfo::exists(arcTempDir + file)) {
0726             if (decompressToFile)
0727                 QFile(arcTempDir + file).remove();
0728             if (encrypted && tries) {
0729                 invalidatePassword();
0730 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0731                 return get(url, tries - 1);
0732             }
0733             return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, getPath(url));
0734 #else
0735                 get(url, tries - 1);
0736                 return;
0737             }
0738             error(KIO::ERR_ACCESS_DENIED, getPath(url));
0739             return;
0740 #endif
0741         }
0742         // the following block is ripped from KDE file KIO::Slave
0743         // $Id: krarc.cpp,v 1.43 2007/01/13 13:39:51 ckarai Exp $
0744         QByteArray _path(QFile::encodeName(arcTempDir + file));
0745         QT_STATBUF buff;
0746         if (QT_LSTAT(_path.data(), &buff) == -1) {
0747             if (errno == EACCES)
0748 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0749                 return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, getPath(url));
0750             return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, getPath(url));
0751         }
0752         if (S_ISDIR(buff.st_mode)) {
0753             return WorkerResult::fail(KIO::ERR_IS_DIRECTORY, getPath(url));
0754         }
0755         if (!S_ISREG(buff.st_mode)) {
0756             return WorkerResult::fail(KIO::ERR_CANNOT_OPEN_FOR_READING, getPath(url));
0757         }
0758         int fd = QT_OPEN(_path.data(), O_RDONLY);
0759         if (fd < 0) {
0760             return WorkerResult::fail(KIO::ERR_CANNOT_OPEN_FOR_READING, getPath(url));
0761         }
0762 #else
0763                 error(KIO::ERR_ACCESS_DENIED, getPath(url));
0764             else
0765                 error(KIO::ERR_DOES_NOT_EXIST, getPath(url));
0766             return;
0767         }
0768         if (S_ISDIR(buff.st_mode)) {
0769             error(KIO::ERR_IS_DIRECTORY, getPath(url));
0770             return;
0771         }
0772         if (!S_ISREG(buff.st_mode)) {
0773             error(KIO::ERR_CANNOT_OPEN_FOR_READING, getPath(url));
0774             return;
0775         }
0776         int fd = QT_OPEN(_path.data(), O_RDONLY);
0777         if (fd < 0) {
0778             error(KIO::ERR_CANNOT_OPEN_FOR_READING, getPath(url));
0779             return;
0780         }
0781 #endif
0782         // Determine the mimetype of the file to be retrieved, and emit it.
0783         // This is mandatory in all slaves (for KRun/BrowserRun to work).
0784         QMimeDatabase db;
0785         QMimeType mt = db.mimeTypeForFile(arcTempDir + file);
0786         if (mt.isValid())
0787             mimeType(mt.name());
0788 
0789         KIO::filesize_t processed_size = 0;
0790 
0791         QString resumeOffset = metaData("resume");
0792         if (!resumeOffset.isEmpty()) {
0793             bool ok;
0794             KIO::fileoffset_t offset = resumeOffset.toLongLong(&ok);
0795             if (ok && (offset > 0) && (offset < buff.st_size)) {
0796                 if (QT_LSEEK(fd, offset, SEEK_SET) == offset) {
0797                     canResume();
0798                     processed_size = offset;
0799                 }
0800             }
0801         }
0802 
0803         totalSize(buff.st_size);
0804 
0805         char buffer[MAX_IPC_SIZE];
0806         while (1) {
0807             int n = int(::read(fd, buffer, MAX_IPC_SIZE));
0808             if (n == -1) {
0809                 if (errno == EINTR)
0810                     continue;
0811 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0812                 ::close(fd);
0813                 return WorkerResult::fail(KIO::ERR_CANNOT_READ, getPath(url));
0814 #else
0815                 error(KIO::ERR_CANNOT_READ, getPath(url));
0816                 ::close(fd);
0817                 return;
0818 #endif
0819             }
0820             if (n == 0)
0821                 break; // Finished
0822 
0823             {
0824                 QByteArray array = QByteArray::fromRawData(buffer, n);
0825                 data(array);
0826             }
0827 
0828             processed_size += n;
0829         }
0830 
0831         data(QByteArray());
0832         ::close(fd);
0833         processedSize(buff.st_size);
0834 
0835 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0836         if (decompressToFile)
0837             QFile(arcTempDir + file).remove();
0838         return WorkerResult::pass();
0839 #else
0840         finished();
0841 
0842         if (decompressToFile)
0843             QFile(arcTempDir + file).remove();
0844         return;
0845 #endif
0846     }
0847     // send empty buffer to mark EOF
0848     data(QByteArray());
0849 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0850     return WorkerResult::pass();
0851 #else
0852     finished();
0853 #endif
0854 }
0855 
0856 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0857 KIO::WorkerResult kio_krarcProtocol::del(QUrl const &url, bool isFile)
0858 #else
0859 void kio_krarcProtocol::del(QUrl const &url, bool isFile)
0860 #endif
0861 {
0862     KRFUNC;
0863     KRDEBUG(getPath(url));
0864 
0865 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0866     const auto writeSupportResult = checkWriteSupport();
0867     if (!writeSupportResult.success())
0868         return writeSupportResult;
0869 
0870     const auto setArcFileResult = setArcFile(url);
0871     if (!setArcFileResult.success()) {
0872         return setArcFileResult;
0873     }
0874     if (newArchiveURL && !initDirDict(url)) {
0875         return WorkerResult::fail(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
0876     }
0877 
0878     if (delCmd.isEmpty()) {
0879         return WorkerResult::fail(ERR_UNSUPPORTED_ACTION, i18n("Deleting files from %1 archives is not supported", arcType));
0880     }
0881     if (!findFileEntry(url)) {
0882         if ((arcType != "arj" && arcType != "lha") || isFile) {
0883             return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, getPath(url));
0884         }
0885     }
0886 #else
0887     if (!checkWriteSupport())
0888         return;
0889 
0890     if (!setArcFile(url)) {
0891         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
0892         return;
0893     }
0894     if (newArchiveURL && !initDirDict(url)) {
0895         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
0896         return;
0897     }
0898 
0899     if (delCmd.isEmpty()) {
0900         error(ERR_UNSUPPORTED_ACTION, i18n("Deleting files from %1 archives is not supported", arcType));
0901         return;
0902     }
0903     if (!findFileEntry(url)) {
0904         if ((arcType != "arj" && arcType != "lha") || isFile) {
0905             error(KIO::ERR_DOES_NOT_EXIST, getPath(url));
0906             return;
0907         }
0908     }
0909 #endif
0910 
0911     QString file = getPath(url).mid(getPath(arcFile->url()).length() + 1);
0912     if (!isFile && file.right(1) != DIR_SEPARATOR) {
0913         if (arcType == "zip")
0914             file = file + DIR_SEPARATOR;
0915     }
0916     KrLinecountingProcess proc;
0917     proc << delCmd << getPath(arcFile->url()) << localeEncodedString(file);
0918     infoMessage(i18n("Deleting %1...", url.fileName()));
0919 
0920     SET_KRCODEC
0921     proc.start();
0922     RESET_KRCODEC
0923 
0924     proc.waitForFinished();
0925     if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) {
0926 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0927         return WorkerResult::fail(ERR_CANNOT_WRITE, getPath(url) + "\n\n" + proc.getErrorMsg());
0928 #else
0929         error(ERR_CANNOT_WRITE, getPath(url) + "\n\n" + proc.getErrorMsg());
0930         return;
0931 #endif
0932     }
0933     //  force a refresh of archive information
0934     initDirDict(url, true);
0935 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0936     return WorkerResult::pass();
0937 #else
0938     finished();
0939 #endif
0940 }
0941 
0942 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0943 KIO::WorkerResult kio_krarcProtocol::stat(const QUrl &url)
0944 #else
0945 void kio_krarcProtocol::stat(const QUrl &url)
0946 #endif
0947 {
0948     KRFUNC;
0949     KRDEBUG(getPath(url));
0950 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0951     const auto setArcFileResult = setArcFile(url);
0952     if (!setArcFileResult.success()) {
0953         return setArcFileResult;
0954     }
0955     if (newArchiveURL && !initDirDict(url)) {
0956         return WorkerResult::fail(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
0957     }
0958 
0959     if (listCmd.isEmpty()) {
0960         return WorkerResult::fail(ERR_UNSUPPORTED_ACTION, i18n("Accessing files is not supported with %1 archives", arcType));
0961     }
0962 #else
0963     if (!setArcFile(url)) {
0964         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
0965         return;
0966     }
0967     if (newArchiveURL && !initDirDict(url)) {
0968         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
0969         return;
0970     }
0971 
0972     if (listCmd.isEmpty()) {
0973         error(ERR_UNSUPPORTED_ACTION, i18n("Accessing files is not supported with %1 archives", arcType));
0974         return;
0975     }
0976 #endif
0977 
0978     QString path = getPath(url, QUrl::StripTrailingSlash);
0979     QUrl newUrl = url;
0980 
0981     // but treat the archive itself as the archive root
0982     if (path == getPath(arcFile->url(), QUrl::StripTrailingSlash)) {
0983         newUrl.setPath(path + DIR_SEPARATOR);
0984         path = getPath(newUrl);
0985     }
0986     // we might be stating a real file
0987     if (QFileInfo::exists(path)) {
0988         QT_STATBUF buff;
0989         QT_STAT(path.toLocal8Bit(), &buff);
0990         QString mime;
0991         QMimeDatabase db;
0992         QMimeType result = db.mimeTypeForFile(path);
0993         if (result.isValid())
0994             mime = result.name();
0995         statEntry(KFileItem(QUrl::fromLocalFile(path), mime, buff.st_mode).entry());
0996 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
0997         return WorkerResult::pass();
0998 #else
0999         finished();
1000         return;
1001 #endif
1002     }
1003     UDSEntry *entry = findFileEntry(newUrl);
1004     if (entry) {
1005         statEntry(*entry);
1006 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1007         return WorkerResult::pass();
1008     }
1009     return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, path);
1010 #else
1011         finished();
1012     } else
1013         error(KIO::ERR_DOES_NOT_EXIST, path);
1014 #endif
1015 }
1016 
1017 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1018 KIO::WorkerResult kio_krarcProtocol::copy(const QUrl &url, const QUrl &dest, int, KIO::JobFlags flags)
1019 #else
1020 void kio_krarcProtocol::copy(const QUrl &url, const QUrl &dest, int, KIO::JobFlags flags)
1021 #endif
1022 {
1023     KRDEBUG("source: " << url.path() << " dest: " << dest.path());
1024 
1025 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1026     const auto writeSupportResult = checkWriteSupport();
1027     if (!writeSupportResult.success())
1028         return writeSupportResult;
1029 #else
1030     if (!checkWriteSupport())
1031         return;
1032 #endif
1033 
1034     bool overwrite = !!(flags & KIO::Overwrite);
1035 
1036     // KDE HACK: opening the password dlg in copy causes error for the COPY, and further problems
1037     // that's why encrypted files are not allowed to copy
1038     if (!encrypted && dest.isLocalFile())
1039         do {
1040             if (url.fileName() != dest.fileName())
1041                 break;
1042 
1043             if (QTextCodec::codecForLocale()->name() != codec->name())
1044                 break;
1045 
1046             // the file exists and we don't want to overwrite
1047             if ((!overwrite) && (QFile(getPath(dest)).exists())) {
1048 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1049                 return WorkerResult::fail((int)ERR_FILE_ALREADY_EXIST, QString(QFile::encodeName(getPath(dest))));
1050             };
1051 
1052             const auto setArcFileResult = setArcFile(url);
1053             if (!setArcFileResult.success()) {
1054                 return setArcFileResult;
1055             }
1056             if (newArchiveURL && !initDirDict(url)) {
1057                 return WorkerResult::fail(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
1058             }
1059 #else
1060                 error((int)ERR_FILE_ALREADY_EXIST, QString(QFile::encodeName(getPath(dest))));
1061                 return;
1062             };
1063 
1064             if (!setArcFile(url)) {
1065                 error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
1066                 return;
1067             }
1068             if (newArchiveURL && !initDirDict(url)) {
1069                 error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
1070                 return;
1071             }
1072 #endif
1073 
1074             UDSEntry *entry = findFileEntry(url);
1075             if (copyCmd.isEmpty() || !entry)
1076                 break;
1077 
1078             QString file = getPath(url).mid(getPath(arcFile->url()).length() + 1);
1079 
1080             QString destDir = getPath(dest, QUrl::StripTrailingSlash);
1081             if (!QDir(destDir).exists()) {
1082                 int ndx = destDir.lastIndexOf(DIR_SEPARATOR_CHAR);
1083                 if (ndx != -1)
1084                     destDir.truncate(ndx + 1);
1085             }
1086 
1087             QDir::setCurrent(destDir);
1088 
1089             QString escapedFilename = file;
1090             if (arcType == "zip") {
1091                 // left bracket needs to be escaped
1092                 escapedFilename.replace('[', "[[]");
1093             }
1094 
1095             KrLinecountingProcess proc;
1096             proc << copyCmd << getPath(arcFile->url(), QUrl::StripTrailingSlash) << escapedFilename;
1097             if (arcType == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!!
1098                 proc.setStandardInputFile("/dev/ptmx");
1099             proc.setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect
1100 
1101             infoMessage(i18n("Unpacking %1...", url.fileName()));
1102             proc.start();
1103             proc.waitForFinished();
1104             if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) {
1105 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1106                 return WorkerResult::fail(KIO::ERR_CANNOT_WRITE, getPath(dest, QUrl::StripTrailingSlash) + "\n\n" + proc.getErrorMsg());
1107 #else
1108                 error(KIO::ERR_CANNOT_WRITE, getPath(dest, QUrl::StripTrailingSlash) + "\n\n" + proc.getErrorMsg());
1109                 return;
1110 #endif
1111             }
1112             if (!QFileInfo::exists(getPath(dest, QUrl::StripTrailingSlash))) {
1113 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1114                 return WorkerResult::fail(KIO::ERR_CANNOT_WRITE, getPath(dest, QUrl::StripTrailingSlash));
1115 #else
1116                 error(KIO::ERR_CANNOT_WRITE, getPath(dest, QUrl::StripTrailingSlash));
1117                 return;
1118 #endif
1119             }
1120 
1121             processedSize(KFileItem(*entry, url).size());
1122 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1123             QDir::setCurrent(QDir::rootPath()); /* for being able to umount devices after copying*/
1124             return WorkerResult::pass();
1125 #else
1126             finished();
1127             QDir::setCurrent(QDir::rootPath()); /* for being able to umount devices after copying*/
1128             return;
1129 #endif
1130 
1131         } while (0);
1132 
1133     if (encrypted)
1134         KRDEBUG("ERROR: " << url.path() << " is encrypted.");
1135     if (!dest.isLocalFile())
1136         KRDEBUG("ERROR: " << url.path() << " is not a local file.");
1137 
1138         // CMD_COPY is no more in KF5 - replaced with 74 value (as stated in https://invent.kde.org/frameworks/kio/-/blob/master/src/core/commands_p.h)
1139 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1140     return WorkerResult::fail(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString("kio_krarc", 74));
1141 #else
1142     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, 74));
1143 #endif
1144 }
1145 
1146 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1147 KIO::WorkerResult kio_krarcProtocol::rename(const QUrl &src, const QUrl &dest, KIO::JobFlags flags)
1148 #else
1149 void kio_krarcProtocol::rename(const QUrl &src, const QUrl &dest, KIO::JobFlags flags)
1150 #endif
1151 {
1152     Q_UNUSED(flags);
1153 
1154     KRDEBUG("renaming from: " << src.path() << " to: " << dest.path());
1155     KRDEBUG("command: " << arcPath);
1156 
1157 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1158     const auto writeSupportResult = checkWriteSupport();
1159     if (!writeSupportResult.success())
1160         return writeSupportResult;
1161 
1162     if (renCmd.isEmpty()) {
1163         return WorkerResult::fail(KIO::ERR_CANNOT_RENAME, src.fileName());
1164     }
1165 
1166     if (src.fileName() == dest.fileName()) {
1167         return WorkerResult::pass();
1168     }
1169 #else
1170     if (!checkWriteSupport()) {
1171         return;
1172     }
1173 
1174     if (renCmd.isEmpty()) {
1175         error(KIO::ERR_CANNOT_RENAME, src.fileName());
1176         return;
1177     }
1178 
1179     if (src.fileName() == dest.fileName()) {
1180         return;
1181     }
1182 #endif
1183 
1184     KrLinecountingProcess proc;
1185     proc << renCmd << arcPath << src.path().remove(arcPath + '/') << dest.path().remove(arcPath + '/');
1186     proc.start();
1187     proc.waitForFinished();
1188 
1189     if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) {
1190 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1191         return WorkerResult::fail(KIO::ERR_CANNOT_RENAME, src.fileName());
1192     }
1193 
1194     return WorkerResult::pass();
1195 #else
1196         error(KIO::ERR_CANNOT_RENAME, src.fileName());
1197         return;
1198     }
1199 
1200     finished();
1201 #endif
1202 }
1203 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1204 KIO::WorkerResult kio_krarcProtocol::listDir(const QUrl &url)
1205 #else
1206 void kio_krarcProtocol::listDir(const QUrl &url)
1207 #endif
1208 {
1209     KRFUNC;
1210     KRDEBUG(getPath(url));
1211 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1212     const auto setArcFileResult = setArcFile(url);
1213     if (!setArcFileResult.success()) {
1214         return setArcFileResult;
1215     }
1216     if (listCmd.isEmpty()) {
1217         return WorkerResult::fail(ERR_UNSUPPORTED_ACTION, i18n("Listing folders is not supported for %1 archives", arcType));
1218 #else
1219     if (!setArcFile(url)) {
1220         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
1221         return;
1222     }
1223     if (listCmd.isEmpty()) {
1224         error(ERR_UNSUPPORTED_ACTION, i18n("Listing folders is not supported for %1 archives", arcType));
1225         return;
1226 #endif
1227     }
1228     QString path = getPath(url);
1229     if (path.right(1) != DIR_SEPARATOR)
1230         path = path + DIR_SEPARATOR;
1231 
1232     // it might be a real dir !
1233     if (QFileInfo::exists(path)) {
1234         if (QFileInfo(path).isDir()) {
1235             QUrl redir;
1236             redir.setPath(getPath(url));
1237             redirection(redir);
1238 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1239             return WorkerResult::pass();
1240         }
1241         // maybe it's an archive !
1242         return WorkerResult::fail(ERR_IS_FILE, path);
1243     }
1244     if (!initDirDict(url)) {
1245         return WorkerResult::fail(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
1246     }
1247 #else
1248             finished();
1249         } else { // maybe it's an archive !
1250             error(ERR_IS_FILE, path);
1251         }
1252         return;
1253     }
1254     if (!initDirDict(url)) {
1255         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
1256         return;
1257     }
1258 #endif
1259 
1260     QString arcDir = path.mid(getPath(arcFile->url()).length());
1261     arcDir.truncate(arcDir.lastIndexOf(DIR_SEPARATOR));
1262     if (arcDir.right(1) != DIR_SEPARATOR)
1263         arcDir = arcDir + DIR_SEPARATOR;
1264 
1265     if (dirDict.find(arcDir) == dirDict.end()) {
1266 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1267         return WorkerResult::fail(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
1268 #else
1269         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
1270         return;
1271 #endif
1272     }
1273     UDSEntryList *dirList = dirDict[arcDir];
1274     totalSize(dirList->size());
1275     listEntries(*dirList);
1276 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1277     return WorkerResult::pass();
1278 #else
1279     finished();
1280 #endif
1281 }
1282 
1283 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1284 KIO::WorkerResult kio_krarcProtocol::setArcFile(const QUrl &url)
1285 #else
1286 bool kio_krarcProtocol::setArcFile(const QUrl &url)
1287 #endif
1288 {
1289     KRFUNC;
1290     KRDEBUG(url.fileName());
1291     QString path = getPath(url);
1292     time_t currTime = time(nullptr);
1293     archiveChanged = true;
1294     newArchiveURL = true;
1295     // is the file already set ?
1296     if (arcFile && getPath(arcFile->url(), QUrl::StripTrailingSlash) == path.left(getPath(arcFile->url(), QUrl::StripTrailingSlash).length())) {
1297         newArchiveURL = false;
1298         // Has it changed ?
1299         KFileItem *newArcFile = new KFileItem(arcFile->url(), QString(), arcFile->mode());
1300         if (metaData("Charset") != currentCharset || !newArcFile->cmp(*arcFile)) {
1301             currentCharset = metaData("Charset");
1302 
1303             codec = QTextCodec::codecForName(currentCharset.toLatin1());
1304             if (codec == nullptr)
1305                 codec = QTextCodec::codecForMib(4 /* latin-1 */);
1306 
1307             delete arcFile;
1308             password.clear();
1309             extArcReady = false;
1310             arcFile = newArcFile;
1311         } else { // same old file
1312             delete newArcFile;
1313             archiveChanged = false;
1314             if (encrypted && password.isNull())
1315 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1316                 (void)initArcParameters();
1317 #else
1318                 initArcParameters();
1319 #endif
1320         }
1321     } else { // it's a new file...
1322         extArcReady = false;
1323 
1324         // new archive file means new dirDict, too
1325         dirDict.clear();
1326 
1327         if (arcFile) {
1328             delete arcFile;
1329             password.clear();
1330             arcFile = nullptr;
1331         }
1332         QString newPath = path;
1333         if (newPath.right(1) != DIR_SEPARATOR)
1334             newPath = newPath + DIR_SEPARATOR;
1335         for (int pos = 0; pos >= 0; pos = newPath.indexOf(DIR_SEPARATOR, pos + 1)) {
1336             QFileInfo qfi(newPath.left(pos));
1337             if (qfi.exists() && !qfi.isDir()) {
1338                 QT_STATBUF stat_p;
1339                 QT_LSTAT(newPath.left(pos).toLocal8Bit(), &stat_p);
1340                 arcFile = new KFileItem(QUrl::fromLocalFile(newPath.left(pos)), QString(), stat_p.st_mode);
1341                 break;
1342             }
1343         }
1344         if (!arcFile) {
1345             // KRDEBUG("ERROR: " << path << " does not exist.");
1346 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1347             return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toString());
1348 #else
1349             error(ERR_DOES_NOT_EXIST, path);
1350             return false; // file not found
1351 #endif
1352         }
1353         currentCharset = metaData("Charset");
1354 
1355         codec = QTextCodec::codecForName(currentCharset.toLatin1());
1356         if (codec == nullptr)
1357             codec = QTextCodec::codecForMib(4 /* latin-1 */);
1358     }
1359 
1360     /* FIX: file change can only be detected if the timestamp between the two consequent
1361        changes is more than 1s. If the archive is continuously changing (check: move files
1362        inside the archive), krarc may erroneously think, that the archive file is unchanged,
1363        because the timestamp is the same as the previous one. This situation can only occur
1364        if the modification time equals with the current time. While this condition is true,
1365        we can say, that the archive is changing, so content reread is always necessary
1366        during that period. */
1367     if (archiveChanging)
1368         archiveChanged = true;
1369     archiveChanging = (currTime == (time_t)arcFile->time(KFileItem::ModificationTime).toTime_t());
1370 
1371     arcPath = getPath(arcFile->url(), QUrl::StripTrailingSlash);
1372     arcType = detectArchive(encrypted, arcPath);
1373 
1374     if (arcType == "tbz")
1375         arcType = "bzip2";
1376     else if (arcType == "tgz")
1377         arcType = "gzip";
1378     else if (arcType == "tlz")
1379         arcType = "lzma";
1380     else if (arcType == "txz")
1381         arcType = "xz";
1382 
1383     if (arcType.isEmpty()) {
1384         arcType = arcFile->mimetype();
1385         arcType = getShortTypeFromMime(arcType);
1386         if (arcType == "jar")
1387             arcType = "zip";
1388     }
1389 
1390     return initArcParameters();
1391 }
1392 
1393 bool kio_krarcProtocol::initDirDict(const QUrl &url, bool forced)
1394 {
1395     KRFUNC;
1396     KRDEBUG(getPath(url));
1397     // set the archive location
1398     // if( !setArcFile(getPath(url)) ) return false;
1399     // no need to rescan the archive if it's not changed
1400     // KRDEBUG("achiveChanged: " << archiveChanged << " forced: " << forced);
1401     if (!archiveChanged && !forced) {
1402         // KRDEBUG("doing nothing.");
1403         return true;
1404     }
1405 
1406     extArcReady = false;
1407 
1408 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1409     const auto setArcFileResult = setArcFile(url);
1410     if (!setArcFileResult.success()) { /* if the archive was changed refresh the file information */
1411         return false;
1412     }
1413 #else
1414     if (!setArcFile(url))
1415         return false; /* if the archive was changed refresh the file information */
1416 #endif
1417 
1418     // write the temp file
1419     KrLinecountingProcess proc;
1420     QTemporaryFile temp;
1421 
1422     // parse the temp file
1423     if (!temp.open()) {
1424 #if KSERVICE_VERSION < QT_VERSION_CHECK(5, 96, 0)
1425         error(ERR_CANNOT_READ, temp.fileName());
1426 #endif
1427         return false;
1428     }
1429 
1430     if (arcType != "bzip2" && arcType != "lzma" && arcType != "xz") {
1431         if (arcType == "rpm") {
1432             proc << listCmd << arcPath;
1433             proc.setStandardOutputFile(temp.fileName());
1434         } else {
1435             proc << listCmd << getPath(arcFile->url(), QUrl::StripTrailingSlash);
1436             proc.setStandardOutputFile(temp.fileName());
1437         }
1438         if (arcType == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!!
1439             proc.setStandardInputFile("/dev/ptmx");
1440 
1441         proc.setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect
1442         proc.start();
1443         proc.waitForFinished();
1444         if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode()))
1445             return false;
1446     }
1447     // clear the dir dictionary
1448 
1449     QHashIterator<QString, KIO::UDSEntryList *> lit(dirDict);
1450     while (lit.hasNext())
1451         delete lit.next().value();
1452     dirDict.clear();
1453 
1454     // add the "/" directory
1455     auto *root = new UDSEntryList();
1456     dirDict.insert(DIR_SEPARATOR, root);
1457     // and the "/" UDSEntry
1458     UDSEntry entry;
1459     entry.fastInsert(KIO::UDSEntry::UDS_NAME, ".");
1460     mode_t mode = parsePermString("drwxr-xr-x");
1461     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, mode & S_IFMT); // keep file type only
1462     entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, mode & 07777); // keep permissions only
1463 
1464     root->append(entry);
1465 
1466     if (arcType == "bzip2" || arcType == "lzma" || arcType == "xz")
1467         abort();
1468 
1469     char buf[1000];
1470     QString line;
1471 
1472     int lineNo = 0;
1473     bool invalidLine = false;
1474     // the rar list is started with a ------ line.
1475     if (arcType == "rar" || arcType == "arj" || arcType == "lha" || arcType == "7z") {
1476         while (temp.readLine(buf, 1000) != -1) {
1477             line = decodeString(buf);
1478             if (line.startsWith(QLatin1String("----------")))
1479                 break;
1480         }
1481     }
1482     while (temp.readLine(buf, 1000) != -1) {
1483         line = decodeString(buf);
1484         if (arcType == "rar") {
1485             // the rar list is ended with a ------ line.
1486             if (line.startsWith(QLatin1String("----------"))) {
1487                 invalidLine = !invalidLine;
1488                 break;
1489             }
1490             if (invalidLine)
1491                 continue;
1492             else {
1493                 if (line[0] == '*') // encrypted archives starts with '*'
1494                     line[0] = ' ';
1495             }
1496         }
1497         if (arcType == "ace") {
1498             // the ace list begins with a number.
1499             if (!line[0].isDigit())
1500                 continue;
1501         }
1502         if (arcType == "arj") {
1503             // the arj list is ended with a ------ line.
1504             if (line.startsWith(QLatin1String("----------"))) {
1505                 invalidLine = !invalidLine;
1506                 continue;
1507             }
1508             if (invalidLine)
1509                 continue;
1510             else {
1511                 temp.readLine(buf, 1000);
1512                 line = line + decodeString(buf);
1513                 temp.readLine(buf, 1000);
1514                 line = line + decodeString(buf);
1515                 temp.readLine(buf, 1000);
1516                 line = line + decodeString(buf);
1517             }
1518         }
1519         if (arcType == "lha" || arcType == "7z") {
1520             // the arj list is ended with a ------ line.
1521             if (line.startsWith(QLatin1String("----------")))
1522                 break;
1523         }
1524         parseLine(lineNo++, line.trimmed());
1525     }
1526     // close and delete our file
1527     temp.close();
1528 
1529     archiveChanged = false;
1530     // KRDEBUG("done.");
1531     return true;
1532 }
1533 
1534 QString kio_krarcProtocol::findArcDirectory(const QUrl &url)
1535 {
1536     KRFUNC;
1537     KRDEBUG(url.fileName());
1538 
1539     QString path = getPath(url);
1540     if (path.right(1) == DIR_SEPARATOR)
1541         path.truncate(path.length() - 1);
1542 
1543     if (!initDirDict(url)) {
1544         return QString();
1545     }
1546     QString arcDir = path.mid(getPath(arcFile->url()).length());
1547     arcDir.truncate(arcDir.lastIndexOf(DIR_SEPARATOR));
1548     if (arcDir.right(1) != DIR_SEPARATOR)
1549         arcDir = arcDir + DIR_SEPARATOR;
1550 
1551     return arcDir;
1552 }
1553 
1554 UDSEntry *kio_krarcProtocol::findFileEntry(const QUrl &url)
1555 {
1556     KRFUNC;
1557     QString arcDir = findArcDirectory(url);
1558     if (arcDir.isEmpty())
1559         return nullptr;
1560 
1561     QHash<QString, KIO::UDSEntryList *>::iterator itef = dirDict.find(arcDir);
1562     if (itef == dirDict.end())
1563         return nullptr;
1564     UDSEntryList *dirList = itef.value();
1565 
1566     QString name = getPath(url);
1567     if (getPath(arcFile->url(), QUrl::StripTrailingSlash) == getPath(url, QUrl::StripTrailingSlash))
1568         name = '.'; // the '/' case
1569     else {
1570         if (name.right(1) == DIR_SEPARATOR)
1571             name.truncate(name.length() - 1);
1572         name = name.mid(name.lastIndexOf(DIR_SEPARATOR) + 1);
1573     }
1574 
1575     UDSEntryList::iterator entry;
1576 
1577     for (entry = dirList->begin(); entry != dirList->end(); ++entry) {
1578         if ((entry->contains(KIO::UDSEntry::UDS_NAME)) && (entry->stringValue(KIO::UDSEntry::UDS_NAME) == name))
1579             return &(*entry);
1580     }
1581     return nullptr;
1582 }
1583 
1584 QString kio_krarcProtocol::nextWord(QString &s, char d)
1585 {
1586     // Note: KRFUNC was not used here in order to avoid filling the log with too much information
1587     s = s.trimmed();
1588     int j = s.indexOf(d, 0);
1589     QString temp = s.left(j); // find the leftmost word.
1590     s.remove(0, j);
1591     return temp;
1592 }
1593 
1594 mode_t kio_krarcProtocol::parsePermString(QString perm)
1595 {
1596     KRFUNC;
1597     mode_t mode = 0;
1598     // file type
1599     if (perm[0] == 'd')
1600         mode |= S_IFDIR;
1601 #ifndef Q_OS_WIN
1602     if (perm[0] == 'l')
1603         mode |= S_IFLNK;
1604 #endif
1605     if (perm[0] == '-')
1606         mode |= S_IFREG;
1607     // owner permissions
1608     if (perm[1] != '-')
1609         mode |= S_IRUSR;
1610     if (perm[2] != '-')
1611         mode |= S_IWUSR;
1612     if (perm[3] != '-')
1613         mode |= S_IXUSR;
1614 #ifndef Q_OS_WIN
1615     // group permissions
1616     if (perm[4] != '-')
1617         mode |= S_IRGRP;
1618     if (perm[5] != '-')
1619         mode |= S_IWGRP;
1620     if (perm[6] != '-')
1621         mode |= S_IXGRP;
1622     // other permissions
1623     if (perm[7] != '-')
1624         mode |= S_IROTH;
1625     if (perm[8] != '-')
1626         mode |= S_IWOTH;
1627     if (perm[9] != '-')
1628         mode |= S_IXOTH;
1629 #endif
1630     return mode;
1631 }
1632 
1633 UDSEntryList *kio_krarcProtocol::addNewDir(const QString &path)
1634 {
1635     KRFUNC;
1636     UDSEntryList *dir;
1637 
1638     // check if the current dir exists
1639     QHash<QString, KIO::UDSEntryList *>::iterator itef = dirDict.find(path);
1640     if (itef != dirDict.end())
1641         return itef.value();
1642 
1643     // set dir to the parent dir
1644     dir = addNewDir(path.left(path.lastIndexOf(DIR_SEPARATOR, -2) + 1));
1645 
1646     // add a new entry in the parent dir
1647     QString name = path.mid(path.lastIndexOf(DIR_SEPARATOR, -2) + 1);
1648     name = name.left(name.length() - 1);
1649 
1650     if (name == "." || name == "..") { // entries with these names wouldn't be displayed
1651         // don't translate since this is an internal error
1652         QString err = QString("Cannot handle path: ") + path;
1653 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
1654         KRDEBUG("ERROR: " << err);
1655 #else
1656         // KRDEBUG("ERROR: " << err);
1657         error(KIO::ERR_INTERNAL, err);
1658 #endif
1659         exit();
1660     }
1661 
1662     UDSEntry entry;
1663     entry.fastInsert(KIO::UDSEntry::UDS_NAME, name);
1664     mode_t mode = parsePermString("drwxr-xr-x");
1665     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, mode & S_IFMT); // keep file type only
1666     entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, mode & 07777); // keep permissions only
1667     entry.fastInsert(KIO::UDSEntry::UDS_SIZE, 0);
1668     entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, arcFile->time(KFileItem::ModificationTime).toTime_t());
1669 
1670     dir->append(entry);
1671 
1672     // create a new directory entry and add it..
1673     dir = new UDSEntryList();
1674     dirDict.insert(path, dir);
1675 
1676     return dir;
1677 }
1678 
1679 void kio_krarcProtocol::parseLine(int lineNo, QString line)
1680 {
1681     KRFUNC;
1682     UDSEntryList *dir;
1683     UDSEntry entry;
1684 
1685     QString owner;
1686     QString group;
1687     QString symlinkDest;
1688     QString perm;
1689     mode_t mode = 0666;
1690     size_t size = 0;
1691     time_t time = ::time(nullptr);
1692     QString fullName;
1693 
1694     if (arcType == "zip") {
1695         // permissions
1696         perm = nextWord(line);
1697         // ignore the next 2 fields
1698         nextWord(line);
1699         nextWord(line);
1700         // size
1701         size = nextWord(line).toLong();
1702         // ignore the next 2 fields
1703         nextWord(line);
1704         nextWord(line);
1705         // date & time
1706         QString d = nextWord(line);
1707         QDate qdate(d.mid(0, 4).toInt(), d.mid(4, 2).toInt(), d.mid(6, 2).toInt());
1708         QTime qtime(d.mid(9, 2).toInt(), d.mid(11, 2).toInt(), d.mid(13, 2).toInt());
1709         time = QDateTime(qdate, qtime).toTime_t();
1710         // full name
1711         fullName = nextWord(line, '\n');
1712 
1713         if (perm.length() != 10)
1714             perm = (perm.at(0) == 'd' || fullName.endsWith(DIR_SEPARATOR)) ? "drwxr-xr-x" : "-rw-r--r--";
1715         mode = parsePermString(perm);
1716     }
1717     if (arcType == "rar") {
1718         // permissions
1719         perm = nextWord(line);
1720         // size
1721         size = nextWord(line).toLong();
1722         // ignore the next 2 fields : packed size and compression ration
1723         nextWord(line);
1724         nextWord(line);
1725         // date & time
1726         QString d = nextWord(line);
1727         QDate qdate(d.left(4).toInt(), d.mid(5, 2).toInt(), d.mid(8, 2).toInt());
1728         QString t = nextWord(line);
1729         QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0);
1730         time = QDateTime(qdate, qtime).toTime_t();
1731         // checksum : ignored
1732         nextWord(line);
1733         // full name
1734         fullName = nextWord(line, '\n');
1735 
1736         if (perm.length() == 7) { // windows rar permission format
1737             bool isDir = (perm.at(1).toLower() == 'd');
1738             bool isReadOnly = (perm.at(2).toLower() == 'r');
1739 
1740             perm = isDir ? "drwxr-xr-x" : "-rw-r--r--";
1741 
1742             if (isReadOnly)
1743                 perm[2] = '-';
1744         }
1745 
1746         if (perm.length() != 10)
1747             perm = (perm.at(0) == 'd') ? "drwxr-xr-x" : "-rw-r--r--";
1748         mode = parsePermString(perm);
1749     }
1750     if (arcType == "arj") {
1751         nextWord(line);
1752         // full name
1753         fullName = nextWord(line, '\n');
1754         // ignore the next 2 fields
1755         nextWord(line);
1756         nextWord(line);
1757         // size
1758         size = nextWord(line).toLong();
1759         // ignore the next 2 fields
1760         nextWord(line);
1761         nextWord(line);
1762         // date & time
1763         QString d = nextWord(line);
1764         int year = 1900 + d.mid(0, 2).toInt();
1765         if (year < 1930)
1766             year += 100;
1767         QDate qdate(year, d.mid(3, 2).toInt(), d.mid(6, 2).toInt());
1768         QString t = nextWord(line);
1769         QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0);
1770         time = QDateTime(qdate, qtime).toTime_t();
1771         // permissions
1772         perm = nextWord(line);
1773         if (perm.length() != 10)
1774             perm = (perm.at(0) == 'd') ? "drwxr-xr-x" : "-rw-r--r--";
1775         mode = parsePermString(perm);
1776     }
1777     if (arcType == "rpm") {
1778         // full name
1779         fullName = nextWord(line);
1780         // size
1781         size = nextWord(line).toULong();
1782         // date & time
1783         time = nextWord(line).toULong();
1784         // next field is md5sum, ignore it
1785         nextWord(line);
1786         // permissions
1787         mode = nextWord(line).toUInt(nullptr, 8);
1788         // Owner & Group
1789         owner = nextWord(line);
1790         group = nextWord(line);
1791         // symlink destination
1792 #ifndef Q_OS_WIN
1793         if (S_ISLNK(mode)) {
1794             // ignore the next 3 fields
1795             nextWord(line);
1796             nextWord(line);
1797             nextWord(line);
1798             symlinkDest = nextWord(line);
1799         }
1800 #endif
1801     }
1802     if (arcType == "gzip") {
1803         if (!lineNo)
1804             return; // ignore the first line
1805         // first field is uncompressed size - ignore it
1806         nextWord(line);
1807         // size
1808         size = nextWord(line).toULong();
1809         // ignore the next field
1810         nextWord(line);
1811         // full name
1812         fullName = nextWord(line);
1813         fullName = fullName.mid(fullName.lastIndexOf(DIR_SEPARATOR) + 1);
1814     }
1815     if (arcType == "lzma") {
1816         fullName = arcFile->name();
1817         if (fullName.endsWith(QLatin1String("lzma"))) {
1818             fullName.truncate(fullName.length() - 5);
1819         }
1820         mode = arcFile->mode();
1821         size = arcFile->size();
1822     }
1823     if (arcType == "xz") {
1824         fullName = arcFile->name();
1825         if (fullName.endsWith(QLatin1String("xz"))) {
1826             fullName.truncate(fullName.length() - 3);
1827         }
1828         mode = arcFile->mode();
1829         size = arcFile->size();
1830     }
1831     if (arcType == "bzip2") {
1832         // There is no way to list bzip2 files, so we take our information from
1833         // the archive itself...
1834         fullName = arcFile->name();
1835         if (fullName.endsWith(QLatin1String("bz2"))) {
1836             fullName.truncate(fullName.length() - 4);
1837         }
1838         mode = arcFile->mode();
1839         size = arcFile->size();
1840     }
1841     if (arcType == "lha") {
1842         // permissions
1843         perm = nextWord(line);
1844         if (perm.length() != 10)
1845             perm = (perm.at(0) == 'd') ? "drwxr-xr-x" : "-rw-r--r--";
1846         mode = parsePermString(perm);
1847         // ignore the next field
1848         nextWord(line);
1849         // size
1850         size = nextWord(line).toLong();
1851         // ignore the next field
1852         nextWord(line);
1853         // date & time
1854         int month = (QString("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec").split(',')).indexOf(nextWord(line)) + 1;
1855         int day = nextWord(line).toInt();
1856         int year = QDate::currentDate().year();
1857         QString third = nextWord(line);
1858         QTime qtime;
1859 
1860         if (third.contains(":"))
1861             qtime = QTime::fromString(third);
1862         else
1863             year = third.toInt();
1864 
1865         QDate qdate(year, month, day);
1866 
1867         time = QDateTime(qdate, qtime).toTime_t();
1868         // full name
1869         fullName = nextWord(line, '\n');
1870     }
1871     if (arcType == "ace") {
1872         // date & time
1873         QString d = nextWord(line);
1874         int year = 1900 + d.mid(6, 2).toInt();
1875         if (year < 1930)
1876             year += 100;
1877         QDate qdate(year, d.mid(3, 2).toInt(), d.mid(0, 2).toInt());
1878         QString t = nextWord(line);
1879         QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0);
1880         time = QDateTime(qdate, qtime).toTime_t();
1881         // ignore the next field
1882         nextWord(line);
1883         // size
1884         size = nextWord(line).toLong();
1885         // ignore the next field
1886         nextWord(line);
1887         // full name
1888         fullName = nextWord(line, '\n');
1889         if (fullName[0] == '*') // encrypted archives starts with '*'
1890             fullName = fullName.mid(1);
1891     }
1892     if (arcType == "deb") {
1893         // permissions
1894         perm = nextWord(line);
1895         mode = parsePermString(perm);
1896         // Owner & Group
1897         owner = nextWord(line, DIR_SEPARATOR_CHAR);
1898         group = nextWord(line).mid(1);
1899         // size
1900         size = nextWord(line).toLong();
1901         // date & time
1902         QString d = nextWord(line);
1903         QDate qdate(d.mid(0, 4).toInt(), d.mid(5, 2).toInt(), d.mid(8, 2).toInt());
1904         QString t = nextWord(line);
1905         QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0);
1906         time = QDateTime(qdate, qtime).toTime_t();
1907         // full name
1908         fullName = nextWord(line, '\n').mid(1);
1909         // if ( fullName.right( 1 ) == "/" ) return;
1910         if (fullName.contains("->")) {
1911             symlinkDest = fullName.mid(fullName.indexOf("->") + 2);
1912             fullName = fullName.left(fullName.indexOf("->") - 1);
1913         }
1914     }
1915     if (arcType == "7z") {
1916         // date & time
1917         QString d = nextWord(line);
1918         QDate qdate(d.mid(0, 4).toInt(), d.mid(5, 2).toInt(), d.mid(8, 2).toInt());
1919         QString t = nextWord(line);
1920         QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), t.mid(6, 2).toInt());
1921         time = QDateTime(qdate, qtime).toTime_t();
1922 
1923         // permissions
1924         perm = nextWord(line);
1925         bool isDir = (perm.at(0).toLower() == 'd');
1926         bool isReadOnly = (perm.at(1).toLower() == 'r');
1927         perm = isDir ? "drwxr-xr-x" : "-rw-r--r--";
1928         if (isReadOnly)
1929             perm[2] = '-';
1930 
1931         mode = parsePermString(perm);
1932 
1933         // size
1934         size = nextWord(line).toLong();
1935 
1936         // ignore the next 15 characters
1937         line = line.mid(15);
1938 
1939         // full name
1940         fullName = nextWord(line, '\n');
1941     }
1942 
1943     if (fullName.right(1) == DIR_SEPARATOR)
1944         fullName = fullName.left(fullName.length() - 1);
1945     if (!fullName.startsWith(DIR_SEPARATOR))
1946         fullName = DIR_SEPARATOR + fullName;
1947     QString path = fullName.left(fullName.lastIndexOf(DIR_SEPARATOR) + 1);
1948     // set/create the directory UDSEntryList
1949     QHash<QString, KIO::UDSEntryList *>::iterator itef = dirDict.find(path);
1950     if (itef == dirDict.end())
1951         dir = addNewDir(path);
1952     else
1953         dir = itef.value();
1954 
1955     QString name = fullName.mid(fullName.lastIndexOf(DIR_SEPARATOR) + 1);
1956     // file name
1957     entry.fastInsert(KIO::UDSEntry::UDS_NAME, name);
1958     // file type
1959     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, mode & S_IFMT); // keep file type only
1960     // file permissions
1961     entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, mode & 07777); // keep permissions only
1962     // file size
1963     entry.fastInsert(KIO::UDSEntry::UDS_SIZE, size);
1964     // modification time
1965     entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, time);
1966     // link destination
1967     if (!symlinkDest.isEmpty()) {
1968         entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, symlinkDest);
1969     }
1970     if (S_ISDIR(mode)) {
1971         fullName = fullName + DIR_SEPARATOR;
1972         if (dirDict.find(fullName) == dirDict.end())
1973             dirDict.insert(fullName, new UDSEntryList());
1974         else {
1975             // try to overwrite an existing entry
1976             UDSEntryList::iterator entryIt;
1977 
1978             for (entryIt = dir->begin(); entryIt != dir->end(); ++entryIt) {
1979                 if (entryIt->contains(KIO::UDSEntry::UDS_NAME) && entryIt->stringValue(KIO::UDSEntry::UDS_NAME) == name) {
1980                     entryIt->fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, time);
1981                     entryIt->fastInsert(KIO::UDSEntry::UDS_ACCESS, mode);
1982                     return;
1983                 }
1984             }
1985             return; // there is already an entry for this directory
1986         }
1987     }
1988 
1989     // multi volume archives can add a file twice, use only one
1990     UDSEntryList::iterator dirEntryIt;
1991 
1992     for (dirEntryIt = dir->begin(); dirEntryIt != dir->end(); ++dirEntryIt)
1993         if (dirEntryIt->contains(KIO::UDSEntry::UDS_NAME) && dirEntryIt->stringValue(KIO::UDSEntry::UDS_NAME) == name)
1994             return;
1995 
1996     dir->append(entry);
1997 }
1998 
1999 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
2000 KIO::WorkerResult kio_krarcProtocol::initArcParameters()
2001 #else
2002 bool kio_krarcProtocol::initArcParameters()
2003 #endif
2004 {
2005     KRFUNC;
2006     KRDEBUG("arcType: " << arcType);
2007 
2008     noencoding = false;
2009 
2010     cmd.clear();
2011     listCmd = QStringList();
2012     getCmd = QStringList();
2013     copyCmd = QStringList();
2014     delCmd = QStringList();
2015     putCmd = QStringList();
2016     renCmd = QStringList();
2017 
2018     if (arcType == "zip") {
2019         noencoding = true;
2020         cmd = fullPathName("unzip");
2021         listCmd << fullPathName("unzip") << "-ZTs-z-t-h";
2022         getCmd << fullPathName("unzip") << "-p";
2023         copyCmd << fullPathName("unzip") << "-jo";
2024 
2025         if (QStandardPaths::findExecutable(QStringLiteral("zip")).isEmpty()) {
2026             delCmd = QStringList();
2027             putCmd = QStringList();
2028         } else {
2029             delCmd << fullPathName("zip") << "-d";
2030             putCmd << fullPathName("zip") << "-ry";
2031         }
2032 
2033         QString a7zExecutable = find7zExecutable();
2034         if (!a7zExecutable.isEmpty()) {
2035             renCmd << a7zExecutable << "rn";
2036         }
2037 
2038         if (!getPassword().isEmpty()) {
2039             getCmd << "-P" << password;
2040             copyCmd << "-P" << password;
2041             putCmd << "-P" << password;
2042         }
2043     } else if (arcType == "rar") {
2044         noencoding = true;
2045         if (QStandardPaths::findExecutable(QStringLiteral("rar")).isEmpty()) {
2046             cmd = fullPathName("unrar");
2047             listCmd << fullPathName("unrar") << "-c-"
2048                     << "-v"
2049                     << "v";
2050             getCmd << fullPathName("unrar") << "p"
2051                    << "-ierr"
2052                    << "-idp"
2053                    << "-c-"
2054                    << "-y";
2055             copyCmd << fullPathName("unrar") << "e"
2056                     << "-y";
2057             delCmd = QStringList();
2058             putCmd = QStringList();
2059         } else {
2060             cmd = fullPathName("rar");
2061             listCmd << fullPathName("rar") << "-c-"
2062                     << "-v"
2063                     << "v";
2064             getCmd << fullPathName("rar") << "p"
2065                    << "-ierr"
2066                    << "-idp"
2067                    << "-c-"
2068                    << "-y";
2069             copyCmd << fullPathName("rar") << "e"
2070                     << "-y";
2071             delCmd << fullPathName("rar") << "d";
2072             putCmd << fullPathName("rar") << "-r"
2073                    << "a";
2074         }
2075         if (!getPassword().isEmpty()) {
2076             getCmd << QString("-p%1").arg(password);
2077             listCmd << QString("-p%1").arg(password);
2078             copyCmd << QString("-p%1").arg(password);
2079             if (!putCmd.isEmpty()) {
2080                 putCmd << QString("-p%1").arg(password);
2081                 delCmd << QString("-p%1").arg(password);
2082             }
2083         }
2084     } else if (arcType == "rpm") {
2085         cmd = fullPathName("rpm");
2086         listCmd << fullPathName("rpm") << "--dump"
2087                 << "-lpq";
2088         getCmd << fullPathName("cpio") << "--force-local"
2089                << "--no-absolute-filenames"
2090                << "-iuvdF";
2091         delCmd = QStringList();
2092         putCmd = QStringList();
2093         copyCmd = QStringList();
2094     } else if (arcType == "gzip") {
2095         cmd = fullPathName("gzip");
2096         listCmd << fullPathName("gzip") << "-l";
2097         getCmd << fullPathName("gzip") << "-dc";
2098         copyCmd = QStringList();
2099         delCmd = QStringList();
2100         putCmd = QStringList();
2101     } else if (arcType == "bzip2") {
2102         cmd = fullPathName("bzip2");
2103         listCmd << fullPathName("bzip2");
2104         getCmd << fullPathName("bzip2") << "-dc";
2105         copyCmd = QStringList();
2106         delCmd = QStringList();
2107         putCmd = QStringList();
2108     } else if (arcType == "lzma") {
2109         cmd = fullPathName("lzma");
2110         listCmd << fullPathName("lzma");
2111         getCmd << fullPathName("lzma") << "-dc";
2112         copyCmd = QStringList();
2113         delCmd = QStringList();
2114         putCmd = QStringList();
2115     } else if (arcType == "xz") {
2116         cmd = fullPathName("xz");
2117         listCmd << fullPathName("xz");
2118         getCmd << fullPathName("xz") << "-dc";
2119         copyCmd = QStringList();
2120         delCmd = QStringList();
2121         putCmd = QStringList();
2122     } else if (arcType == "arj") {
2123         cmd = fullPathName("arj");
2124         listCmd << fullPathName("arj") << "v"
2125                 << "-y"
2126                 << "-v";
2127         getCmd << fullPathName("arj") << "-jyov"
2128                << "-v"
2129                << "e";
2130         copyCmd << fullPathName("arj") << "-jyov"
2131                 << "-v"
2132                 << "e";
2133         delCmd << fullPathName("arj") << "d";
2134         putCmd << fullPathName("arj") << "-r"
2135                << "a";
2136         if (!getPassword().isEmpty()) {
2137             getCmd << QString("-g%1").arg(password);
2138             copyCmd << QString("-g%1").arg(password);
2139             putCmd << QString("-g%1").arg(password);
2140         }
2141     } else if (arcType == "lha") {
2142         cmd = fullPathName("lha");
2143         listCmd << fullPathName("lha") << "l";
2144         getCmd << fullPathName("lha") << "pq";
2145         copyCmd << fullPathName("lha") << "eif";
2146         delCmd << fullPathName("lha") << "d";
2147         putCmd << fullPathName("lha") << "a";
2148     } else if (arcType == "ace") {
2149         cmd = fullPathName("unace");
2150         listCmd << fullPathName("unace") << "v";
2151         getCmd << fullPathName("unace") << "e"
2152                << "-o";
2153         copyCmd << fullPathName("unace") << "e"
2154                 << "-o";
2155         delCmd = QStringList();
2156         putCmd = QStringList();
2157         if (!getPassword().isEmpty()) {
2158             getCmd << QString("-p%1").arg(password);
2159             copyCmd << QString("-p%1").arg(password);
2160         }
2161     } else if (arcType == "deb") {
2162         cmd = fullPathName("dpkg");
2163         listCmd << fullPathName("dpkg") << "-c";
2164         getCmd << fullPathName("tar") << "xvf";
2165         copyCmd = QStringList();
2166         delCmd = QStringList();
2167         putCmd = QStringList();
2168     } else if (arcType == "7z") {
2169         noencoding = true;
2170         cmd = find7zExecutable();
2171         if (cmd.isEmpty()) {
2172 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
2173             return WorkerResult::fail(KIO::ERR_CANNOT_LAUNCH_PROCESS, {});
2174 #else
2175             return false;
2176 #endif
2177         }
2178 
2179         listCmd << cmd << "l"
2180                 << "-y";
2181         getCmd << cmd << "e"
2182                << "-y";
2183         copyCmd << cmd << "e"
2184                 << "-y";
2185         delCmd << cmd << "d"
2186                << "-y";
2187         putCmd << cmd << "a"
2188                << "-y";
2189         renCmd << cmd << "rn";
2190         if (!getPassword().isEmpty()) {
2191             getCmd << QString("-p%1").arg(password);
2192             listCmd << QString("-p%1").arg(password);
2193             copyCmd << QString("-p%1").arg(password);
2194             if (!putCmd.isEmpty()) {
2195                 putCmd << QString("-p%1").arg(password);
2196                 delCmd << QString("-p%1").arg(password);
2197             }
2198         }
2199     }
2200     // checking if it's an absolute path
2201 #ifdef Q_OS_WIN
2202     if (cmd.length() > 2 && cmd[0].isLetter() && cmd[1] == ':')
2203 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
2204         return WorkerResult::pass();
2205 #else
2206         return true;
2207 #endif
2208 
2209 #else
2210     if (cmd.startsWith(DIR_SEPARATOR))
2211 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
2212         return WorkerResult::pass();
2213 #else
2214         return true;
2215 #endif
2216 
2217 #endif
2218     if (QStandardPaths::findExecutable(cmd).isEmpty()) {
2219 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
2220         KRDEBUG("Failed to find cmd: " << cmd);
2221         return WorkerResult::fail(KIO::ERR_CANNOT_LAUNCH_PROCESS, cmd + i18n("\nMake sure that the %1 binary is installed properly on your system.", cmd));
2222     }
2223     return WorkerResult::pass();
2224 #else
2225         error(KIO::ERR_CANNOT_LAUNCH_PROCESS, cmd + i18n("\nMake sure that the %1 binary is installed properly on your system.", cmd));
2226         KRDEBUG("Failed to find cmd: " << cmd);
2227         return false;
2228     }
2229     return true;
2230 #endif
2231 }
2232 
2233 bool kio_krarcProtocol::checkStatus(int exitCode)
2234 {
2235     KRFUNC;
2236     KRDEBUG(exitCode);
2237     return KrArcBaseManager::checkStatus(arcType, exitCode);
2238 }
2239 
2240 void kio_krarcProtocol::checkIf7zIsEncrypted(bool &encrypted, QString fileName)
2241 {
2242     // Reminder: If that function is modified, it's important to research if the
2243     // changes must also be applied to `KrArcHandler::checkIf7zIsEncrypted()`
2244 
2245     KRFUNC;
2246     if (encryptedArchPath == fileName)
2247         encrypted = true;
2248     else { // we try to find whether the 7z archive is encrypted
2249         // this is hard as the headers are also compressed
2250         QString a7zExecutable = find7zExecutable();
2251         if (a7zExecutable.isEmpty()) {
2252             return;
2253         }
2254 
2255         lastData = encryptedArchPath = "";
2256 
2257         KrLinecountingProcess proc;
2258         // Note: That command uses information given in a comment from
2259         // https://stackoverflow.com/questions/5248572/how-do-i-know-if-7zip-used-aes256
2260         proc << a7zExecutable << "l"
2261              << "-slt" << fileName;
2262         connect(&proc, &KrLinecountingProcess::newOutputData, this, &kio_krarcProtocol::check7zOutputForPassword);
2263         proc.start();
2264         proc.waitForFinished();
2265         encrypted = this->encrypted;
2266 
2267         if (encrypted)
2268             encryptedArchPath = fileName;
2269     }
2270 }
2271 
2272 void kio_krarcProtocol::check7zOutputForPassword(KProcess *proc, QByteArray &buf)
2273 {
2274     // Reminder: If that function is modified, it's important to research if the
2275     // changes must also be applied to `Kr7zEncryptionChecker::receivedOutput()`
2276 
2277     KRFUNC;
2278     QString data = QString(buf);
2279 
2280     QString checkable = lastData + data;
2281 
2282     QStringList lines = checkable.split('\n');
2283     lastData = lines[lines.count() - 1];
2284     for (int i = 0; i != lines.count(); i++) {
2285         QString line = lines[i].trimmed().toLower();
2286         int ndx = line.indexOf("listing"); // Reminder: Lower-case letters are used
2287         if (ndx >= 0)
2288             line.truncate(ndx);
2289         if (line.isEmpty())
2290             continue;
2291 
2292         if ((line.contains("password") && line.contains("enter")) || line == QStringLiteral("encrypted = +")) {
2293             KRDEBUG("Encrypted 7z archive found!");
2294             encrypted = true;
2295             proc->kill();
2296             return;
2297         }
2298     }
2299 }
2300 
2301 void kio_krarcProtocol::invalidatePassword()
2302 {
2303     KRFUNC;
2304     KRDEBUG(getPath(arcFile->url(), QUrl::StripTrailingSlash) + DIR_SEPARATOR);
2305 
2306     if (!encrypted)
2307         return;
2308 
2309     KIO::AuthInfo authInfo;
2310     authInfo.caption = i18n("Krarc Password Dialog");
2311     authInfo.username = "archive";
2312     authInfo.readOnly = true;
2313     authInfo.keepPassword = true;
2314     authInfo.verifyPath = true;
2315     QString fileName = getPath(arcFile->url(), QUrl::StripTrailingSlash);
2316     authInfo.url = QUrl::fromLocalFile(ROOT_DIR);
2317     authInfo.url.setHost(fileName /*.replace('/','_')*/);
2318     authInfo.url.setScheme("krarc");
2319 
2320     password.clear();
2321 
2322     cacheAuthentication(authInfo);
2323 }
2324 
2325 QString kio_krarcProtocol::getPassword()
2326 {
2327     KRFUNC;
2328     KRDEBUG("Encrypted: " << encrypted);
2329 
2330     if (!password.isNull())
2331         return password;
2332     if (!encrypted)
2333         return (password = "");
2334 
2335     KIO::AuthInfo authInfo;
2336     authInfo.caption = i18n("Krarc Password Dialog");
2337     authInfo.username = "archive";
2338     authInfo.readOnly = true;
2339     authInfo.keepPassword = true;
2340     authInfo.verifyPath = true;
2341     QString fileName = getPath(arcFile->url(), QUrl::StripTrailingSlash);
2342     authInfo.url = QUrl::fromLocalFile(ROOT_DIR);
2343     authInfo.url.setHost(fileName /*.replace('/','_')*/);
2344     authInfo.url.setScheme("krarc");
2345 
2346     if (checkCachedAuthentication(authInfo) && !authInfo.password.isNull()) {
2347         KRDEBUG(authInfo.password);
2348         return (password = authInfo.password);
2349     }
2350 
2351     authInfo.password.clear();
2352 
2353 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 96, 0)
2354     int errCode = openPasswordDialog(authInfo, i18n("Accessing the file requires a password."));
2355     if (!errCode && !authInfo.password.isNull()) {
2356         KRDEBUG(authInfo.password);
2357         return (password = authInfo.password);
2358     } else {
2359         password.clear();
2360     }
2361 #else
2362 
2363 #if KIO_VERSION_MINOR >= 24
2364     int errCode = openPasswordDialogV2(authInfo, i18n("Accessing the file requires a password."));
2365     if (!errCode && !authInfo.password.isNull()) {
2366 #else
2367     if (openPasswordDialog(authInfo, i18n("Accessing the file requires a password.")) && !authInfo.password.isNull()) {
2368 #endif
2369         KRDEBUG(authInfo.password);
2370         return (password = authInfo.password);
2371 #if KIO_VERSION_MINOR >= 24
2372     } else {
2373         error(errCode, QString());
2374 #endif
2375     }
2376 
2377 #endif
2378 
2379     KRDEBUG(password);
2380     return password;
2381 }
2382 
2383 QString kio_krarcProtocol::localeEncodedString(QString str)
2384 {
2385     // Note: KRFUNC was not used here in order to avoid filling the log with too much information
2386     if (noencoding)
2387         return str;
2388 
2389     QByteArray array = codec->fromUnicode(str);
2390 
2391     // encoding the byte array to QString, mapping 0x0000-0x00FF to 0xE000-0xE0FF
2392     // see KrArcCodec for more explanation
2393     int size = array.size();
2394     QString result;
2395 
2396     const char *data = array.data();
2397     for (int i = 0; i != size; i++) {
2398         unsigned int ch = (((int)data[i]) & 0xFF) + 0xE000; // user defined character
2399         result.append(QChar(ch));
2400     }
2401     return result;
2402 }
2403 
2404 QByteArray kio_krarcProtocol::encodeString(const QString &str)
2405 {
2406     // Note: KRFUNC was not used here in order to avoid filling the log with too much information
2407     if (noencoding)
2408         return QTextCodec::codecForLocale()->fromUnicode(str);
2409     return codec->fromUnicode(str);
2410 }
2411 
2412 QString kio_krarcProtocol::decodeString(char *buf)
2413 {
2414     // Note: KRFUNC was not used here in order to avoid filling the log with too much information
2415     if (noencoding)
2416         return QTextCodec::codecForLocale()->toUnicode(buf);
2417     return codec->toUnicode(buf);
2418 }
2419 
2420 QString kio_krarcProtocol::getPath(const QUrl &url, QUrl::FormattingOptions options)
2421 {
2422     // Note: KRFUNC was not used here in order to avoid filling the log with too much information
2423     QString path = url.adjusted(options).path();
2424     REPLACE_DIR_SEP2(path);
2425 
2426 #ifdef Q_OS_WIN
2427     if (path.startsWith(DIR_SEPARATOR)) {
2428         int p = 1;
2429         while (p < path.length() && path[p] == DIR_SEPARATOR_CHAR)
2430             p++;
2431         /* /C:/Folder */
2432         if (p + 2 <= path.length() && path[p].isLetter() && path[p + 1] == ':') {
2433             path = path.mid(p);
2434         }
2435     }
2436 #endif
2437     return path;
2438 }
2439 
2440 #endif // KRARC_ENABLED
2441 
2442 #include "krarc.moc"