File indexing completed on 2019-09-16 10:41:10

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