File indexing completed on 2020-07-03 20:38:33

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