File indexing completed on 2024-04-21 16:32:41

0001 /*
0002     SPDX-FileCopyrightText: 2001 Shie Erlich <krusader@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2001 Rafi Yanai <krusader@users.sourceforge.net>
0004     SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "krarchandler.h"
0010 
0011 // QtCore
0012 #include <QDebug>
0013 #include <QDir>
0014 #include <QFile>
0015 // QtWidgets
0016 #include <QApplication>
0017 
0018 #include <KConfigCore/KSharedConfig>
0019 #include <KI18n/KLocalizedString>
0020 #include <KIO/Global>
0021 #include <KIOCore/KProtocolManager>
0022 #include <KWallet/KWallet>
0023 #include <KWidgetsAddons/KMessageBox>
0024 #include <KWidgetsAddons/KPasswordDialog>
0025 #include <utility>
0026 
0027 #include "../../plugins/krarc/krlinecountingprocess.h"
0028 #include "../Dialogs/krpleasewait.h"
0029 #include "../defaults.h"
0030 #include "../krglobal.h"
0031 #include "../krservices.h"
0032 #include "kr7zencryptionchecker.h"
0033 
0034 static QStringList arcProtocols = QString("tar;bzip;bzip2;lzma;xz;gzip;krarc;zip").split(';');
0035 
0036 QMap<QString, QString> *KrArcHandler::slaveMap = nullptr;
0037 KWallet::Wallet *KrArcHandler::wallet = nullptr;
0038 
0039 KrArcHandler::KrArcHandler(QObject *parent)
0040     : QObject(parent)
0041 {
0042     // Reminder: If a mime type is added/removed/modified in that
0043     // member function, it's important to research if the type has to
0044     // be added/removed/modified in the `krarc.protocol` file, or
0045     // in `KrArcBaseManager::getShortTypeFromMime(const QString &mime)`
0046 
0047     // Hard-code these proven mimetypes openable by krarc protocol.
0048     // They cannot be listed in krarc.protocol itself
0049     // because it would baffle other file managers (like Dolphin).
0050     krarcArchiveMimetypes = {
0051         QStringLiteral("application/x-deb"),
0052         QStringLiteral("application/x-debian-package"),
0053         QStringLiteral("application/vnd.debian.binary-package"),
0054         QStringLiteral("application/x-java-archive"),
0055         QStringLiteral("application/x-rpm"),
0056         QStringLiteral("application/x-source-rpm"),
0057         QStringLiteral("application/vnd.oasis.opendocument.chart"),
0058         QStringLiteral("application/vnd.oasis.opendocument.database"),
0059         QStringLiteral("application/vnd.oasis.opendocument.formula"),
0060         QStringLiteral("application/vnd.oasis.opendocument.graphics"),
0061         QStringLiteral("application/vnd.oasis.opendocument.presentation"),
0062         QStringLiteral("application/vnd.oasis.opendocument.spreadsheet"),
0063         QStringLiteral("application/vnd.oasis.opendocument.text"),
0064         QStringLiteral("application/vnd.openxmlformats-officedocument.presentationml.presentation"),
0065         QStringLiteral("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),
0066         QStringLiteral("application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
0067         QStringLiteral("application/x-cbz"),
0068         QStringLiteral("application/vnd.comicbook+zip"),
0069         QStringLiteral("application/x-cbr"),
0070         QStringLiteral("application/vnd.comicbook-rar"),
0071         QStringLiteral("application/epub+zip"),
0072         QStringLiteral("application/x-webarchive"),
0073         QStringLiteral("application/x-plasma"),
0074         QStringLiteral("application/vnd.rar"),
0075     };
0076 
0077 #ifdef KRARC_QUERY_ENABLED
0078     const auto mimetypes = KProtocolInfo::archiveMimetypes("krarc");
0079     for (const auto &mimetype : mimetypes)
0080         krarcArchiveMimetypes.insert(mimetype);
0081 #endif
0082 }
0083 
0084 QStringList KrArcHandler::supportedPackers()
0085 {
0086     QStringList packers;
0087 
0088     // we will simply try to find the packers here..
0089     if (KrServices::cmdExist("tar"))
0090         packers.append("tar");
0091     if (KrServices::cmdExist("gzip"))
0092         packers.append("gzip");
0093     if (KrServices::cmdExist("bzip2"))
0094         packers.append("bzip2");
0095     if (KrServices::cmdExist("lzma"))
0096         packers.append("lzma");
0097     if (KrServices::cmdExist("xz"))
0098         packers.append("xz");
0099     if (KrServices::cmdExist("unzip"))
0100         packers.append("unzip");
0101     if (KrServices::cmdExist("zip"))
0102         packers.append("zip");
0103     if (KrServices::cmdExist("zip"))
0104         packers.append("cbz");
0105     if (KrServices::cmdExist("lha"))
0106         packers.append("lha");
0107     if (KrServices::cmdExist("cpio"))
0108         packers.append("cpio");
0109     if (KrServices::cmdExist("unrar"))
0110         packers.append("unrar");
0111     if (KrServices::cmdExist("rar"))
0112         packers.append("rar");
0113     if (KrServices::cmdExist("rar"))
0114         packers.append("cbr");
0115     if (KrServices::cmdExist("arj"))
0116         packers.append("arj");
0117     if (KrServices::cmdExist("unarj"))
0118         packers.append("unarj");
0119     if (KrServices::cmdExist("unace"))
0120         packers.append("unace");
0121     if (KrServices::cmdExist("dpkg"))
0122         packers.append("dpkg");
0123     if (KrServices::cmdExist("7z") || KrServices::cmdExist("7za"))
0124         packers.append("7z");
0125     if (KrServices::cmdExist("rpm") && KrServices::cmdExist("rpm2cpio"))
0126         packers.append("rpm");
0127     // qDebug() << "Supported Packers:";
0128     // QStringList::Iterator it;
0129     // for( it = packers.begin(); it != packers.end(); ++it )
0130     // qDebug() << *it;
0131 
0132     return packers;
0133 }
0134 
0135 bool KrArcHandler::arcSupported(QString type)
0136 {
0137     // lst will contain the supported unpacker list...
0138     const KConfigGroup group(krConfig, "Archives");
0139     const QStringList lst = group.readEntry("Supported Packers", QStringList());
0140 
0141     // Let's notice that in some cases the QString `type` that arrives here
0142     // represents a mimetype, and in some other cases it represents
0143     // a short identifier.
0144     // If `type` is not a short identifier then it's supposed that `type` is a mime type
0145     if (type.length() > maxLenType) {
0146         type = getShortTypeFromMime(type);
0147     }
0148 
0149     // clang-format off
0150     return (type == "zip" && lst.contains("unzip"))
0151            || (type == "tar" && lst.contains("tar"))
0152            || (type == "tbz" && lst.contains("tar"))
0153            || (type == "tgz" && lst.contains("tar"))
0154            || (type == "tlz" && lst.contains("tar"))
0155            || (type == "txz" && lst.contains("tar"))
0156            || (type == "tarz" && lst.contains("tar"))
0157            || (type == "gzip" && lst.contains("gzip"))
0158            || (type == "bzip2" && lst.contains("bzip2"))
0159            || (type == "lzma" && lst.contains("lzma"))
0160            || (type == "xz" && lst.contains("xz"))
0161            || (type == "lha" && lst.contains("lha"))
0162            || (type == "ace" && lst.contains("unace"))
0163            || (type == "rpm" && lst.contains("cpio"))
0164            || (type == "cpio" && lst.contains("cpio"))
0165            || (type == "rar" && (lst.contains("unrar") || lst.contains("rar")))
0166            || (type == "arj" && (lst.contains("unarj") || lst.contains("arj")))
0167            || (type == "deb" && (lst.contains("dpkg") && lst.contains("tar")))
0168            || (type == "7z" && lst.contains("7z"));
0169     // clang-format on
0170 }
0171 
0172 qulonglong KrArcHandler::arcFileCount(const QString &archive, const QString &type, const QString &password, KrArcObserver *observer)
0173 {
0174     int divideWith = 1;
0175 
0176     // first check if supported
0177     if (!arcSupported(type))
0178         return 0;
0179 
0180     // bzip2, gzip, etc. archives contain only one file
0181     if (type == "bzip2" || type == "gzip" || type == "lzma" || type == "xz")
0182         return 1L;
0183 
0184     // set the right lister to do the job
0185     QStringList lister;
0186 
0187     if (type == "zip")
0188         lister << KrServices::fullPathName("unzip") << "-ZTs";
0189     else if (type == "tar")
0190         lister << KrServices::fullPathName("tar") << "-tvf";
0191     else if (type == "tgz")
0192         lister << KrServices::fullPathName("tar") << "-tvzf";
0193     else if (type == "tarz")
0194         lister << KrServices::fullPathName("tar") << "-tvzf";
0195     else if (type == "tbz")
0196         lister << KrServices::fullPathName("tar") << "-tjvf";
0197     else if (type == "tlz")
0198         lister << KrServices::fullPathName("tar") << "--lzma"
0199                << "-tvf";
0200     else if (type == "txz")
0201         lister << KrServices::fullPathName("tar") << "--xz"
0202                << "-tvf";
0203     else if (type == "lha")
0204         lister << KrServices::fullPathName("lha") << "l";
0205     else if (type == "rar")
0206         lister << KrServices::fullPathName(KrServices::cmdExist("rar") ? "rar" : "unrar") << "l"
0207                << "-v";
0208     else if (type == "ace")
0209         lister << KrServices::fullPathName("unace") << "l";
0210     else if (type == "arj") {
0211         if (KrServices::cmdExist("arj")) {
0212             lister << KrServices::fullPathName("arj") << "v"
0213                    << "-y"
0214                    << "-v";
0215             divideWith = 4;
0216         } else {
0217             lister << KrServices::fullPathName("unarj") << "l";
0218         }
0219     } else if (type == "rpm")
0220         lister << KrServices::fullPathName("rpm") << "--dump"
0221                << "-lpq";
0222     else if (type == "deb")
0223         lister << KrServices::fullPathName("dpkg") << "-c";
0224     else if (type == "7z")
0225         lister << find7zExecutable() << "-y"
0226                << "l";
0227     else
0228         return 0L;
0229 
0230     if (!password.isNull()) {
0231         if (type == "arj")
0232             lister << QString("-g%1").arg(password);
0233         if (type == "ace" || type == "rar" || type == "7z")
0234             lister << QString("-p%1").arg(password);
0235     }
0236 
0237     // tell the user to wait
0238     observer->subJobStarted(i18n("Counting files in archive"), 0);
0239 
0240     // count the number of files in the archive
0241     qulonglong count = 1;
0242     KProcess list;
0243     list << lister << archive;
0244     if (type == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!!
0245         list.setStandardInputFile("/dev/ptmx");
0246     list.setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect
0247     list.start();
0248     // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking
0249     // it would be better to connect to started(), error() and finished()
0250     if (list.waitForStarted())
0251         while (list.state() == QProcess::Running) {
0252             observer->processEvents();
0253             if (observer->wasCancelled())
0254                 list.kill();
0255         }; // busy wait - need to find something better...
0256 
0257     observer->subJobStopped();
0258 
0259     if (list.exitStatus() != QProcess::NormalExit || !checkStatus(type, list.exitCode())) {
0260         observer->detailedError(i18n("Failed to list the content of the archive (%1).", archive), QString::fromLocal8Bit(list.readAllStandardError()));
0261         return 0;
0262     }
0263 
0264     count = list.readAllStandardOutput().count('\n');
0265 
0266     // make sure you call stopWait after this function return...
0267     //   observer->subJobStopped();
0268 
0269     return count / divideWith;
0270 }
0271 
0272 bool KrArcHandler::unpack(QString archive, const QString &type, const QString &password, const QString &dest, KrArcObserver *observer)
0273 {
0274     KConfigGroup group(krConfig, "Archives");
0275     if (group.readEntry("Test Before Unpack", _TestBeforeUnpack)) {
0276         // test first - or be sorry later...
0277         if (type != "rpm" && type != "deb" && !test(archive, type, password, observer, 0)) {
0278             observer->error(i18n("Failed to unpack %1.", archive));
0279             return false;
0280         }
0281     }
0282 
0283     // count the files in the archive
0284     qulonglong count = arcFileCount(archive, type, password, observer);
0285     if (count == 0)
0286         return false; // not supported
0287     if (count == 1)
0288         count = 0;
0289 
0290     // choose the right packer for the job
0291     QString cpioName;
0292     QStringList packer;
0293 
0294     // set the right packer to do the job
0295     if (type == "zip")
0296         packer << KrServices::fullPathName("unzip") << "-o";
0297     else if (type == "tar")
0298         packer << KrServices::fullPathName("tar") << "-xvf";
0299     else if (type == "tgz")
0300         packer << KrServices::fullPathName("tar") << "-xvzf";
0301     else if (type == "tarz")
0302         packer << KrServices::fullPathName("tar") << "-xvzf";
0303     else if (type == "tbz")
0304         packer << KrServices::fullPathName("tar") << "-xjvf";
0305     else if (type == "tlz")
0306         packer << KrServices::fullPathName("tar") << "--lzma"
0307                << "-xvf";
0308     else if (type == "txz")
0309         packer << KrServices::fullPathName("tar") << "--xz"
0310                << "-xvf";
0311     else if (type == "gzip")
0312         packer << KrServices::fullPathName("gzip") << "-cd";
0313     else if (type == "bzip2")
0314         packer << KrServices::fullPathName("bzip2") << "-cdk";
0315     else if (type == "lzma")
0316         packer << KrServices::fullPathName("lzma") << "-cdk";
0317     else if (type == "xz")
0318         packer << KrServices::fullPathName("xz") << "-cdk";
0319     else if (type == "lha")
0320         packer << KrServices::fullPathName("lha") << "xf";
0321     else if (type == "rar")
0322         packer << KrServices::fullPathName(KrServices::cmdExist("rar") ? "rar" : "unrar") << "-y"
0323                << "x";
0324     else if (type == "ace")
0325         packer << KrServices::fullPathName("unace") << "x";
0326     else if (type == "arj") {
0327         if (KrServices::cmdExist("arj"))
0328             packer << KrServices::fullPathName("arj") << "-y"
0329                    << "-v"
0330                    << "x";
0331         else
0332             packer << KrServices::fullPathName("unarj") << "x";
0333     } else if (type == "7z")
0334         packer << find7zExecutable() << "-y"
0335                << "x";
0336     else if (type == "rpm") {
0337         // TODO use QTemporaryFile (setAutoRemove(false) when asynchrone)
0338         cpioName = QDir::tempPath() + QStringLiteral("/contents.cpio");
0339 
0340         KrLinecountingProcess cpio;
0341         cpio << KrServices::fullPathName("rpm2cpio") << archive;
0342         cpio.setStandardOutputFile(cpioName); // TODO maybe no tmpfile but a pipe (setStandardOutputProcess(packer))
0343         cpio.start();
0344         if (!cpio.waitForFinished() || cpio.exitStatus() != QProcess::NormalExit || !checkStatus("cpio", cpio.exitCode())) {
0345             observer->detailedError(i18n("Failed to convert rpm (%1) to cpio.", archive), cpio.getErrorMsg());
0346             return 0;
0347         }
0348 
0349         archive = cpioName;
0350         packer << KrServices::fullPathName("cpio") << "--force-local"
0351                << "--no-absolute-filenames"
0352                << "-iuvdF";
0353     } else if (type == "deb") {
0354         // TODO use QTemporaryFile (setAutoRemove(false) when asynchrone)
0355         cpioName = QDir::tempPath() + QStringLiteral("/contents.tar");
0356 
0357         KrLinecountingProcess dpkg;
0358         dpkg << KrServices::fullPathName("dpkg") << "--fsys-tarfile" << archive;
0359         dpkg.setStandardOutputFile(cpioName); // TODO maybe no tmpfile but a pipe (setStandardOutputProcess(packer))
0360         dpkg.start();
0361         if (!dpkg.waitForFinished() || dpkg.exitStatus() != QProcess::NormalExit || !checkStatus("deb", dpkg.exitCode())) {
0362             observer->detailedError(i18n("Failed to convert deb (%1) to tar.", archive), dpkg.getErrorMsg());
0363             return 0;
0364         }
0365 
0366         archive = cpioName;
0367         packer << KrServices::fullPathName("tar") << "xvf";
0368     } else
0369         return false;
0370 
0371     if (!password.isNull()) {
0372         if (type == "zip")
0373             packer << "-P" << password;
0374         if (type == "arj")
0375             packer << QString("-g%1").arg(password);
0376         if (type == "ace" || type == "rar" || type == "7z")
0377             packer << QString("-p%1").arg(password);
0378     }
0379 
0380     // unpack the files
0381     KrLinecountingProcess proc;
0382     proc << packer << archive;
0383     if (type == "bzip2" || type == "gzip" || type == "lzma" || type == "xz") {
0384         QString arcname = archive.mid(archive.lastIndexOf("/") + 1);
0385         if (arcname.contains("."))
0386             arcname = arcname.left(arcname.lastIndexOf("."));
0387         proc.setStandardOutputFile(dest + '/' + arcname);
0388     }
0389     if (type == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!!
0390         proc.setStandardInputFile("/dev/ptmx");
0391 
0392     proc.setWorkingDirectory(dest);
0393 
0394     // tell the user to wait
0395     observer->subJobStarted(i18n("Unpacking File(s)"), count);
0396     if (count != 0) {
0397         connect(&proc, &KrLinecountingProcess::newOutputLines, observer, &KrArcObserver::incrementProgress);
0398         if (type == "rpm")
0399             connect(&proc, &KrLinecountingProcess::newErrorLines, observer, &KrArcObserver::incrementProgress);
0400     }
0401 
0402     // start the unpacking process
0403     proc.start();
0404     // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking
0405     // it would be better to connect to started(), error() and finished()
0406     if (proc.waitForStarted())
0407         while (proc.state() == QProcess::Running) {
0408             observer->processEvents();
0409             if (observer->wasCancelled())
0410                 proc.kill();
0411         }; // busy wait - need to find something better...
0412     observer->subJobStopped();
0413 
0414     if (!cpioName.isEmpty())
0415         QFile(cpioName).remove(); /* remove the cpio file */
0416 
0417     // check the return value
0418     if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(type, proc.exitCode())) {
0419         observer->detailedError(i18n("Failed to unpack %1.", archive), observer->wasCancelled() ? i18n("User cancelled.") : proc.getErrorMsg());
0420         return false;
0421     }
0422     return true; // SUCCESS
0423 }
0424 
0425 bool KrArcHandler::test(const QString &archive, const QString &type, const QString &password, KrArcObserver *observer, qulonglong count)
0426 {
0427     // choose the right packer for the job
0428     QStringList packer;
0429 
0430     // set the right packer to do the job
0431     if (type == "zip")
0432         packer << KrServices::fullPathName("unzip") << "-t";
0433     else if (type == "tar")
0434         packer << KrServices::fullPathName("tar") << "-tvf";
0435     else if (type == "tgz")
0436         packer << KrServices::fullPathName("tar") << "-tvzf";
0437     else if (type == "tarz")
0438         packer << KrServices::fullPathName("tar") << "-tvzf";
0439     else if (type == "tbz")
0440         packer << KrServices::fullPathName("tar") << "-tjvf";
0441     else if (type == "tlz")
0442         packer << KrServices::fullPathName("tar") << "--lzma"
0443                << "-tvf";
0444     else if (type == "txz")
0445         packer << KrServices::fullPathName("tar") << "--xz"
0446                << "-tvf";
0447     else if (type == "gzip")
0448         packer << KrServices::fullPathName("gzip") << "-tv";
0449     else if (type == "bzip2")
0450         packer << KrServices::fullPathName("bzip2") << "-tv";
0451     else if (type == "lzma")
0452         packer << KrServices::fullPathName("lzma") << "-tv";
0453     else if (type == "xz")
0454         packer << KrServices::fullPathName("xz") << "-tv";
0455     else if (type == "rar")
0456         packer << KrServices::fullPathName(KrServices::cmdExist("rar") ? "rar" : "unrar") << "t";
0457     else if (type == "ace")
0458         packer << KrServices::fullPathName("unace") << "t";
0459     else if (type == "lha")
0460         packer << KrServices::fullPathName("lha") << "t";
0461     else if (type == "arj")
0462         packer << KrServices::fullPathName(KrServices::cmdExist("arj") ? "arj" : "unarj") << "t";
0463     else if (type == "cpio")
0464         packer << KrServices::fullPathName("cpio") << "--only-verify-crc"
0465                << "-tvF";
0466     else if (type == "7z")
0467         packer << find7zExecutable() << "-y"
0468                << "t";
0469     else
0470         return false;
0471 
0472     if (!password.isNull()) {
0473         if (type == "zip")
0474             packer << "-P" << password;
0475         if (type == "arj")
0476             packer << QString("-g%1").arg(password);
0477         if (type == "ace" || type == "rar" || type == "7z")
0478             packer << QString("-p%1").arg(password);
0479     }
0480 
0481     // unpack the files
0482     KrLinecountingProcess proc;
0483     proc << packer << archive;
0484 
0485     if (type == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!!
0486         proc.setStandardInputFile("/dev/ptmx");
0487 
0488     // tell the user to wait
0489     observer->subJobStarted(i18n("Testing Archive"), count);
0490     if (count != 0)
0491         connect(&proc, &KrLinecountingProcess::newOutputLines, observer, &KrArcObserver::incrementProgress);
0492 
0493     // start the unpacking process
0494     proc.start();
0495     // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking
0496     // it would be better to connect to started(), error() and finished()
0497     if (proc.waitForStarted())
0498         while (proc.state() == QProcess::Running) {
0499             observer->processEvents();
0500             if (observer->wasCancelled())
0501                 proc.kill();
0502         }; // busy wait - need to find something better...
0503     observer->subJobStopped();
0504 
0505     // check the return value
0506     if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(type, proc.exitCode()))
0507         return false;
0508 
0509     return true; // SUCCESS
0510 }
0511 
0512 bool KrArcHandler::pack(QStringList fileNames, QString type, const QString &dest, qulonglong count, QMap<QString, QString> extraProps, KrArcObserver *observer)
0513 {
0514     // set the right packer to do the job
0515     QStringList packer;
0516 
0517     if (type == "zip") {
0518         packer << KrServices::fullPathName("zip") << "-ry";
0519     } else if (type == "cbz") {
0520         packer << KrServices::fullPathName("zip") << "-ry";
0521         type = "zip";
0522     } else if (type == "tar") {
0523         packer << KrServices::fullPathName("tar") << "-cvf";
0524     } else if (type == "tar.gz") {
0525         packer << KrServices::fullPathName("tar") << "-cvzf";
0526         type = "tgz";
0527     } else if (type == "tar.bz2") {
0528         packer << KrServices::fullPathName("tar") << "-cvjf";
0529         type = "tbz";
0530     } else if (type == "tar.lzma") {
0531         packer << KrServices::fullPathName("tar") << "--lzma"
0532                << "-cvf";
0533         type = "tlz";
0534     } else if (type == "tar.xz") {
0535         packer << KrServices::fullPathName("tar") << "--xz"
0536                << "-cvf";
0537         type = "txz";
0538     } else if (type == "rar") {
0539         packer << KrServices::fullPathName("rar") << "-r"
0540                << "a";
0541     } else if (type == "cbr") {
0542         packer << KrServices::fullPathName("rar") << "-r"
0543                << "a";
0544         type = "rar";
0545     } else if (type == "lha") {
0546         packer << KrServices::fullPathName("lha") << "a";
0547     } else if (type == "arj") {
0548         packer << KrServices::fullPathName("arj") << "-r"
0549                << "-y"
0550                << "a";
0551     } else if (type == "7z") {
0552         packer << find7zExecutable() << "-y"
0553                << "a";
0554     } else
0555         return false;
0556 
0557     QString password;
0558 
0559     if (extraProps.count("Password") > 0) {
0560         password = extraProps["Password"];
0561 
0562         if (!password.isNull()) {
0563             if (type == "zip")
0564                 packer << "-P" << password;
0565             else if (type == "arj")
0566                 packer << QString("-g%1").arg(password);
0567             else if (type == "ace" || type == "7z")
0568                 packer << QString("-p%1").arg(password);
0569             else if (type == "rar") {
0570                 if (extraProps.count("EncryptHeaders") > 0)
0571                     packer << QString("-hp%1").arg(password);
0572                 else
0573                     packer << QString("-p%1").arg(password);
0574             } else
0575                 password.clear();
0576         }
0577     }
0578 
0579     if (extraProps.count("VolumeSize") > 0) {
0580         QString sizeStr = extraProps["VolumeSize"];
0581         KIO::filesize_t size = sizeStr.toLongLong();
0582 
0583         if (size >= 10000) {
0584             if (type == "arj" || type == "rar")
0585                 packer << QString("-v%1b").arg(sizeStr);
0586         }
0587     }
0588 
0589     if (extraProps.count("CompressionLevel") > 0) {
0590         int level = extraProps["CompressionLevel"].toInt() - 1;
0591         if (level < 0)
0592             level = 0;
0593         if (level > 8)
0594             level = 8;
0595 
0596         if (type == "rar") {
0597             static const int rarLevels[] = {0, 1, 2, 2, 3, 3, 4, 4, 5};
0598             packer << QString("-m%1").arg(rarLevels[level]);
0599         } else if (type == "arj") {
0600             static const int arjLevels[] = {0, 4, 4, 3, 3, 2, 2, 1, 1};
0601             packer << QString("-m%1").arg(arjLevels[level]);
0602         } else if (type == "zip") {
0603             static const int zipLevels[] = {0, 1, 2, 4, 5, 6, 7, 8, 9};
0604             packer << QString("-%1").arg(zipLevels[level]);
0605         } else if (type == "7z") {
0606             static const int sevenZipLevels[] = {0, 1, 2, 4, 5, 6, 7, 8, 9};
0607             packer << QString("-mx%1").arg(sevenZipLevels[level]);
0608         }
0609     }
0610 
0611     if (extraProps.count("CommandLineSwitches") > 0)
0612         packer << QString("%1").arg(extraProps["CommandLineSwitches"]);
0613 
0614     // prepare to pack
0615     KrLinecountingProcess proc;
0616     proc << packer << dest;
0617 
0618     for (auto &fileName : fileNames) {
0619         proc << fileName;
0620     }
0621 
0622     // tell the user to wait
0623     observer->subJobStarted(i18n("Packing File(s)"), count);
0624     if (count != 0)
0625         connect(&proc, &KrLinecountingProcess::newOutputLines, observer, &KrArcObserver::incrementProgress);
0626 
0627     // start the packing process
0628     proc.start();
0629     // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking
0630     // it would be better to connect to started(), error() and finished()
0631     if (proc.waitForStarted())
0632         while (proc.state() == QProcess::Running) {
0633             observer->processEvents();
0634             if (observer->wasCancelled())
0635                 proc.kill();
0636         }; // busy wait - need to find something better...
0637     observer->subJobStopped();
0638 
0639     // check the return value
0640     if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(type, proc.exitCode())) {
0641         observer->detailedError(i18n("Failed to pack %1.", dest), observer->wasCancelled() ? i18n("User cancelled.") : proc.getErrorMsg());
0642         return false;
0643     }
0644 
0645     KConfigGroup group(krConfig, "Archives");
0646     if (group.readEntry("Test Archives", _TestArchives) && !test(dest, type, password, observer, count)) {
0647         observer->error(i18n("Failed to pack %1.", dest));
0648         return false;
0649     }
0650     return true; // SUCCESS
0651 }
0652 
0653 bool KrArcHandler::openWallet()
0654 {
0655     if (!wallet) {
0656         // find a suitable parent window
0657         QWidget *actWindow = QApplication::activeWindow();
0658         if (!actWindow)
0659             actWindow = (QWidget *)QApplication::desktop();
0660 
0661         wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), actWindow->effectiveWinId());
0662     }
0663     return (wallet != nullptr);
0664 }
0665 
0666 QString KrArcHandler::getPassword(const QString &path)
0667 {
0668     QString password;
0669 
0670     QString key = "krarc-" + path;
0671 
0672     if (!KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), KWallet::Wallet::PasswordFolder(), key)) {
0673         if (!KWallet::Wallet::isOpen(KWallet::Wallet::NetworkWallet()) && wallet != nullptr) {
0674             delete wallet;
0675             wallet = nullptr;
0676         }
0677         if (openWallet() && wallet->hasFolder(KWallet::Wallet::PasswordFolder())) {
0678             wallet->setFolder(KWallet::Wallet::PasswordFolder());
0679             QMap<QString, QString> map;
0680             if (wallet->readMap(key, map) == 0) {
0681                 QMap<QString, QString>::const_iterator it = map.constFind("password");
0682                 if (it != map.constEnd())
0683                     password = it.value();
0684             }
0685         }
0686     }
0687 
0688     bool keep = true;
0689     QString user = "archive";
0690     QPointer<KPasswordDialog> passDlg = new KPasswordDialog(nullptr, KPasswordDialog::ShowKeepPassword);
0691     passDlg->setPrompt(i18n("This archive is encrypted, please supply the password:")), passDlg->setUsername(user);
0692     passDlg->setPassword(password);
0693     if (passDlg->exec() == KPasswordDialog::Accepted) {
0694         password = passDlg->password();
0695         if (keep) {
0696             if (!KWallet::Wallet::isOpen(KWallet::Wallet::NetworkWallet()) && wallet != nullptr) {
0697                 delete wallet;
0698                 wallet = nullptr;
0699             }
0700             if (openWallet()) {
0701                 bool ok = true;
0702                 if (!wallet->hasFolder(KWallet::Wallet::PasswordFolder()))
0703                     ok = wallet->createFolder(KWallet::Wallet::PasswordFolder());
0704                 if (ok) {
0705                     wallet->setFolder(KWallet::Wallet::PasswordFolder());
0706                     QMap<QString, QString> map;
0707                     map.insert("login", "archive");
0708                     map.insert("password", password);
0709                     wallet->writeMap(key, map);
0710                 }
0711             }
0712         }
0713         delete passDlg;
0714         return password;
0715     }
0716     delete passDlg;
0717 
0718     return "";
0719 }
0720 
0721 bool KrArcHandler::isArchive(const QUrl &url)
0722 {
0723     QString protocol = url.scheme();
0724     if (arcProtocols.indexOf(protocol) != -1)
0725         return true;
0726     else
0727         return false;
0728 }
0729 
0730 QString KrArcHandler::getType(bool &encrypted, QString fileName, const QString &mime, bool check7zEncrypted, bool fast)
0731 {
0732     QString result = detectArchive(encrypted, std::move(fileName), check7zEncrypted, fast);
0733     if (result.isNull()) {
0734         // Then the type is based on the mime type
0735         return getShortTypeFromMime(mime);
0736     }
0737     return result;
0738 }
0739 
0740 bool KrArcHandler::checkStatus(const QString &type, int exitCode)
0741 {
0742     return KrArcBaseManager::checkStatus(type, exitCode);
0743 }
0744 
0745 void KrArcHandler::checkIf7zIsEncrypted(bool &encrypted, QString fileName)
0746 {
0747     // Reminder: If that function is modified, it's important to research if the
0748     // changes must also be applied to `kio_krarcProtocol::checkIf7zIsEncrypted()`
0749 
0750     Kr7zEncryptionChecker proc;
0751     // TODO incorporate all this in Kr7zEncryptionChecker
0752     // Note: That command uses information given in a comment from
0753     // https://stackoverflow.com/questions/5248572/how-do-i-know-if-7zip-used-aes256
0754     proc << find7zExecutable() << "l"
0755          << "-slt" << fileName;
0756     proc.start();
0757     proc.waitForFinished();
0758     encrypted = proc.isEncrypted();
0759 }
0760 
0761 QString KrArcHandler::registeredProtocol(const QString &mimetype)
0762 {
0763     if (slaveMap == nullptr) {
0764         slaveMap = new QMap<QString, QString>();
0765 
0766         KConfigGroup group(krConfig, "Protocols");
0767         QStringList protList = group.readEntry("Handled Protocols", QStringList());
0768         for (auto &it : protList) {
0769             QStringList mimes = group.readEntry(QString("Mimes For %1").arg(it), QStringList());
0770             for (auto &mime : mimes)
0771                 (*slaveMap)[mime] = it;
0772         }
0773     }
0774     QString protocol = (*slaveMap)[mimetype];
0775     if (protocol.isEmpty()) {
0776         if (krarcArchiveMimetypes.contains(mimetype)) {
0777             return QStringLiteral("krarc");
0778         }
0779         protocol = KProtocolManager::protocolForArchiveMimetype(mimetype);
0780     }
0781     return protocol;
0782 }
0783 
0784 void KrArcHandler::clearProtocolCache()
0785 {
0786     if (slaveMap)
0787         delete slaveMap;
0788     slaveMap = nullptr;
0789 }