krarchandler.cpp 31.1 KB
Newer Older
1 2 3
/*****************************************************************************
 * Copyright (C) 2001 Shie Erlich <krusader@users.sourceforge.net>           *
 * Copyright (C) 2001 Rafi Yanai <krusader@users.sourceforge.net>            *
Nikita Melnichenko's avatar
Nikita Melnichenko committed
4
 * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org]              *
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *                                                                           *
 * This file is part of Krusader [https://krusader.org].                     *
 *                                                                           *
 * Krusader is free software: you can redistribute it and/or modify          *
 * it under the terms of the GNU General Public License as published by      *
 * the Free Software Foundation, either version 2 of the License, or         *
 * (at your option) any later version.                                       *
 *                                                                           *
 * Krusader is distributed in the hope that it will be useful,               *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 * GNU General Public License for more details.                              *
 *                                                                           *
 * You should have received a copy of the GNU General Public License         *
 * along with Krusader.  If not, see [http://www.gnu.org/licenses/].         *
 *****************************************************************************/
Fathi Boudra's avatar
Fathi Boudra committed
21 22 23

#include "krarchandler.h"

24 25 26 27 28 29
// QtCore
#include <QDebug>
#include <QDir>
#include <QFile>
// QtWidgets
#include <QApplication>
Fathi Boudra's avatar
Fathi Boudra committed
30

31
#include <KArchive/KTar>
32
#include <KConfigCore/KSharedConfig>
Simon Persson's avatar
Simon Persson committed
33
#include <KI18n/KLocalizedString>
34
#include <KIO/Global>
35
#include <KIOCore/KProtocolManager>
36
#include <KWallet/KWallet>
37
#include <KWidgetsAddons/KMessageBox>
38
#include <KWidgetsAddons/KPasswordDialog>
39
#include <utility>
Fathi Boudra's avatar
Fathi Boudra committed
40

41
#include "kr7zencryptionchecker.h"
42
#include "../krglobal.h"
Rafi Yanai's avatar
Rafi Yanai committed
43
#include "../defaults.h"
44
#include "../krservices.h"
45
#include "../Dialogs/krpleasewait.h"
46
#include "../../krArc/krlinecountingprocess.h"
Rafi Yanai's avatar
Rafi Yanai committed
47

48
#if 0
49 50 51
class DefaultKRarcObserver : public KRarcObserver
{
public:
Fathi Boudra's avatar
Fathi Boudra committed
52 53 54
    DefaultKRarcObserver() {}
    virtual ~DefaultKRarcObserver() {}

55
    virtual void processEvents() override {
Fathi Boudra's avatar
Fathi Boudra committed
56 57 58 59
        usleep(1000);
        qApp->processEvents();
    }

60
    virtual void subJobStarted(const QString & jobTitle, int count) override {
Fathi Boudra's avatar
Fathi Boudra committed
61 62 63
        krApp->startWaiting(jobTitle, count, true);
    }

64
    virtual void subJobStopped() override {
Fathi Boudra's avatar
Fathi Boudra committed
65 66 67
        krApp->stopWait();
    }

68
    virtual bool wasCancelled() override {
Fathi Boudra's avatar
Fathi Boudra committed
69 70 71
        return krApp->wasWaitingCancelled();
    }

72
    virtual void error(const QString & error) override {
Fathi Boudra's avatar
Fathi Boudra committed
73 74 75
        KMessageBox::error(krApp, error, i18n("Error"));
    }

76
    virtual void detailedError(const QString & error, const QString & details) override {
Fathi Boudra's avatar
Fathi Boudra committed
77 78 79
        KMessageBox::detailedError(krApp, error, details, i18n("Error"));
    }

80
    virtual void incrementProgress(int c) override {
Fathi Boudra's avatar
Fathi Boudra committed
81 82
        krApp->plzWait->incProgress(c);
    }
83
};
84
#endif
85

86
static QStringList arcProtocols = QString("tar;bzip;bzip2;lzma;xz;gzip;krarc;zip").split(';');
87

88 89
QMap<QString, QString>* KrArcHandler::slaveMap = nullptr;
KWallet::Wallet * KrArcHandler::wallet = nullptr;
90

91
KrArcHandler::KrArcHandler(QObject *parent) : QObject(parent)
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
{
    // Reminder: If a mime type is added/removed/modified in that
    // member function, it's important to research if the type has to
    // be added/removed/modified in the `krarc.protocol` file, or
    // in `KrArcBaseManager::getShortTypeFromMime(const QString &mime)`

    // Hard-code these proven mimetypes openable by krarc protocol.
    // They cannot be listed in krarc.protocol itself
    // because it would baffle other file managers (like Dolphin).
    krarcArchiveMimetypes = { QStringLiteral("application/x-deb"),
                              QStringLiteral("application/x-debian-package"),
                              QStringLiteral("application/vnd.debian.binary-package"),
                              QStringLiteral("application/x-java-archive"),
                              QStringLiteral("application/x-rpm"),
                              QStringLiteral("application/x-source-rpm"),
                              QStringLiteral("application/vnd.oasis.opendocument.chart"),
                              QStringLiteral("application/vnd.oasis.opendocument.database"),
                              QStringLiteral("application/vnd.oasis.opendocument.formula"),
                              QStringLiteral("application/vnd.oasis.opendocument.graphics"),
                              QStringLiteral("application/vnd.oasis.opendocument.presentation"),
                              QStringLiteral("application/vnd.oasis.opendocument.spreadsheet"),
                              QStringLiteral("application/vnd.oasis.opendocument.text"),
                              QStringLiteral("application/vnd.openxmlformats-officedocument.presentationml.presentation"),
                              QStringLiteral("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),
                              QStringLiteral("application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
                              QStringLiteral("application/x-cbz"),
                              QStringLiteral("application/vnd.comicbook+zip"),
                              QStringLiteral("application/x-cbr"),
                              QStringLiteral("application/vnd.comicbook-rar"),
                              QStringLiteral("application/epub+zip"),
                              QStringLiteral("application/x-webarchive"),
                              QStringLiteral("application/x-plasma"),
                              QStringLiteral("application/vnd.rar") };

    #ifdef KRARC_QUERY_ENABLED
    krarcArchiveMimetypes += QSet<QString>::fromList(KProtocolInfo::archiveMimetypes("krarc"));
    #endif
}

131
QStringList KrArcHandler::supportedPackers()
Fathi Boudra's avatar
Fathi Boudra committed
132 133 134 135 136 137 138 139
{
    QStringList packers;

    // we will simply try to find the packers here..
    if (KrServices::cmdExist("tar")) packers.append("tar");
    if (KrServices::cmdExist("gzip")) packers.append("gzip");
    if (KrServices::cmdExist("bzip2")) packers.append("bzip2");
    if (KrServices::cmdExist("lzma")) packers.append("lzma");
140
    if (KrServices::cmdExist("xz")) packers.append("xz");
Fathi Boudra's avatar
Fathi Boudra committed
141 142
    if (KrServices::cmdExist("unzip")) packers.append("unzip");
    if (KrServices::cmdExist("zip")) packers.append("zip");
Davide Gianforte's avatar
Davide Gianforte committed
143
    if (KrServices::cmdExist("zip")) packers.append("cbz");
Fathi Boudra's avatar
Fathi Boudra committed
144 145 146 147
    if (KrServices::cmdExist("lha")) packers.append("lha");
    if (KrServices::cmdExist("cpio")) packers.append("cpio");
    if (KrServices::cmdExist("unrar")) packers.append("unrar");
    if (KrServices::cmdExist("rar")) packers.append("rar");
Davide Gianforte's avatar
Davide Gianforte committed
148
    if (KrServices::cmdExist("rar")) packers.append("cbr");
Fathi Boudra's avatar
Fathi Boudra committed
149 150 151 152 153 154
    if (KrServices::cmdExist("arj")) packers.append("arj");
    if (KrServices::cmdExist("unarj")) packers.append("unarj");
    if (KrServices::cmdExist("unace")) packers.append("unace");
    if (KrServices::cmdExist("dpkg")) packers.append("dpkg");
    if (KrServices::cmdExist("7z") || KrServices::cmdExist("7za")) packers.append("7z");
    if (KrServices::cmdExist("rpm") && KrServices::cmdExist("rpm2cpio")) packers.append("rpm");
155
    // qDebug() << "Supported Packers:";
Fathi Boudra's avatar
Fathi Boudra committed
156 157
    //QStringList::Iterator it;
    //for( it = packers.begin(); it != packers.end(); ++it )
158
    // qDebug() << *it;
Fathi Boudra's avatar
Fathi Boudra committed
159 160 161 162

    return packers;
}

163
bool KrArcHandler::arcSupported(QString type)
Fathi Boudra's avatar
Fathi Boudra committed
164 165
{
    // lst will contain the supported unpacker list...
166 167
    const KConfigGroup group(krConfig, "Archives");
    const QStringList lst = group.readEntry("Supported Packers", QStringList());
Fathi Boudra's avatar
Fathi Boudra committed
168

169 170 171 172 173 174
    // Let's notice that in some cases the QString `type` that arrives here
    // represents a mimetype, and in some other cases it represents
    // a short identifier.
    // If `type` is not a short identifier then it's supposed that `type` is a mime type
    if (type.length() > maxLenType) {
        type = getShortTypeFromMime(type);
175 176
    }

177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
    return (type == "zip" && lst.contains("unzip"))
           || (type == "tar" && lst.contains("tar"))
           || (type == "tbz" && lst.contains("tar"))
           || (type == "tgz" && lst.contains("tar"))
           || (type == "tlz" && lst.contains("tar"))
           || (type == "txz" && lst.contains("tar"))
           || (type == "tarz" && lst.contains("tar"))
           || (type == "gzip" && lst.contains("gzip"))
           || (type == "bzip2" && lst.contains("bzip2"))
           || (type == "lzma" && lst.contains("lzma"))
           || (type == "xz" && lst.contains("xz"))
           || (type == "lha" && lst.contains("lha"))
           || (type == "ace" && lst.contains("unace"))
           || (type == "rpm" && lst.contains("cpio"))
           || (type == "cpio" && lst.contains("cpio"))
           || (type == "rar" && (lst.contains("unrar") || lst.contains("rar")))
           || (type == "arj" && (lst.contains("unarj") || lst.contains("arj")))
           || (type == "deb" && (lst.contains("dpkg") && lst.contains("tar")))
           || (type == "7z" && lst.contains("7z"));
Fathi Boudra's avatar
Fathi Boudra committed
196 197
}

198
long KrArcHandler::arcFileCount(const QString& archive, const QString& type, const QString& password, KRarcObserver *observer)
Fathi Boudra's avatar
Fathi Boudra committed
199 200 201 202
{
    int divideWith = 1;

    // first check if supported
203 204
    if (!arcSupported(type))
        return 0;
Fathi Boudra's avatar
Fathi Boudra committed
205

206 207 208
    // bzip2, gzip, etc. archives contain only one file
    if (type == "bzip2" || type == "gzip" || type == "lzma" || type == "xz")
        return 1L;
Fathi Boudra's avatar
Fathi Boudra committed
209 210 211 212

    // set the right lister to do the job
    QStringList lister;

213 214 215
    if (type == "zip") lister << KrServices::fullPathName("unzip") << "-ZTs";
    else if (type == "tar") lister << KrServices::fullPathName("tar") << "-tvf";
    else if (type == "tgz") lister << KrServices::fullPathName("tar") << "-tvzf";
Fathi Boudra's avatar
Fathi Boudra committed
216
    else if (type == "tarz") lister << KrServices::fullPathName("tar") << "-tvzf";
217 218 219 220 221 222 223
    else if (type == "tbz") lister << KrServices::fullPathName("tar") << "-tjvf";
    else if (type == "tlz") lister << KrServices::fullPathName("tar") << "--lzma" << "-tvf";
    else if (type == "txz") lister << KrServices::fullPathName("tar") << "--xz" << "-tvf";
    else if (type == "lha") lister << KrServices::fullPathName("lha") << "l";
    else if (type == "rar") lister << KrServices::fullPathName(KrServices::cmdExist("rar") ? "rar" : "unrar") << "l" << "-v";
    else if (type == "ace") lister << KrServices::fullPathName("unace") << "l";
    else if (type == "arj") {
Fathi Boudra's avatar
Fathi Boudra committed
224 225 226 227 228
        if (KrServices::cmdExist("arj"))
            lister << KrServices::fullPathName("arj") << "v" << "-y" << "-v",
            divideWith = 4;
        else
            lister << KrServices::fullPathName("unarj") << "l";
229 230
    } else if (type == "rpm") lister << KrServices::fullPathName("rpm") << "--dump" << "-lpq";
    else if (type == "deb") lister << KrServices::fullPathName("dpkg") << "-c";
231
    else if (type == "7z")  lister << find7zExecutable() << "-y" << "l";
Fathi Boudra's avatar
Fathi Boudra committed
232 233 234
    else return 0L;

    if (!password.isNull()) {
235
        if (type == "arj")
Fathi Boudra's avatar
Fathi Boudra committed
236
            lister << QString("-g%1").arg(password);
237
        if (type == "ace" || type == "rar" || type == "7z")
Fathi Boudra's avatar
Fathi Boudra committed
238
            lister << QString("-p%1").arg(password);
239
    }
Fathi Boudra's avatar
Fathi Boudra committed
240 241 242 243 244 245 246 247

    // tell the user to wait
    observer->subJobStarted(i18n("Counting files in archive"), 0);

    // count the number of files in the archive
    long count = 1;
    KProcess list;
    list << lister << archive;
248
    if (type == "ace" && QFile("/dev/ptmx").exists())     // Don't remove, unace crashes if missing!!!
Fathi Boudra's avatar
Fathi Boudra committed
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
        list.setStandardInputFile("/dev/ptmx");
    list.setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect
    list.start();
    // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking
    // it would be better to connect to started(), error() and finished()
    if (list.waitForStarted()) while (list.state() == QProcess::Running) {
            observer->processEvents();
            if (observer->wasCancelled())
                list.kill();
        }
    ; // busy wait - need to find something better...

    observer->subJobStopped();

    if (list.exitStatus() != QProcess::NormalExit || !checkStatus(type, list.exitCode())) {
Pino Toscano's avatar
Pino Toscano committed
264
        observer->detailedError(i18n("Failed to list the content of the archive (%1).", archive),
Fathi Boudra's avatar
Fathi Boudra committed
265 266
                                QString::fromLocal8Bit(list.readAllStandardError()));
        return 0;
267 268
    }

Fathi Boudra's avatar
Fathi Boudra committed
269 270 271 272 273 274 275 276
    count = list.readAllStandardOutput().count('\n');

    //make sure you call stopWait after this function return...
    //  observer->subJobStopped();

    return count / divideWith;
}

277
bool KrArcHandler::unpack(QString archive, const QString& type, const QString& password, const QString& dest, KRarcObserver *observer)
Fathi Boudra's avatar
Fathi Boudra committed
278 279 280 281
{
    KConfigGroup group(krConfig, "Archives");
    if (group.readEntry("Test Before Unpack", _TestBeforeUnpack)) {
        // test first - or be sorry later...
282
        if (type != "rpm" && type != "deb" && !test(archive, type, password, observer, 0)) {
Pino Toscano's avatar
Pino Toscano committed
283
            observer->error(i18n("Failed to unpack %1.", archive));
Fathi Boudra's avatar
Fathi Boudra committed
284 285
            return false;
        }
286
    }
Fathi Boudra's avatar
Fathi Boudra committed
287 288 289

    // count the files in the archive
    long count = arcFileCount(archive, type, password, observer);
290 291 292 293
    if (count == 0)
        return false;   // not supported
    if (count == 1)
        count = 0;
Fathi Boudra's avatar
Fathi Boudra committed
294 295 296 297 298 299

    // choose the right packer for the job
    QString cpioName;
    QStringList packer;

    // set the right packer to do the job
300
    if (type == "zip") packer << KrServices::fullPathName("unzip") << "-o";
301 302
    else if (type == "tar") packer << KrServices::fullPathName("tar") << "-xvf";
    else if (type == "tgz") packer << KrServices::fullPathName("tar") << "-xvzf";
Fathi Boudra's avatar
Fathi Boudra committed
303
    else if (type == "tarz") packer << KrServices::fullPathName("tar") << "-xvzf";
304 305 306
    else if (type == "tbz") packer << KrServices::fullPathName("tar") << "-xjvf";
    else if (type == "tlz") packer << KrServices::fullPathName("tar") << "--lzma" << "-xvf";
    else if (type == "txz") packer << KrServices::fullPathName("tar") << "--xz" << "-xvf";
Fathi Boudra's avatar
Fathi Boudra committed
307
    else if (type == "gzip") packer << KrServices::fullPathName("gzip") << "-cd";
308
    else if (type == "bzip2") packer << KrServices::fullPathName("bzip2") << "-cdk";
Fathi Boudra's avatar
Fathi Boudra committed
309
    else if (type == "lzma") packer << KrServices::fullPathName("lzma") << "-cdk";
310 311 312 313 314
    else if (type == "xz")  packer << KrServices::fullPathName("xz") << "-cdk";
    else if (type == "lha") packer << KrServices::fullPathName("lha") << "xf";
    else if (type == "rar") packer << KrServices::fullPathName(KrServices::cmdExist("rar") ? "rar" : "unrar") << "-y" << "x";
    else if (type == "ace") packer << KrServices::fullPathName("unace") << "x";
    else if (type == "arj") {
Fathi Boudra's avatar
Fathi Boudra committed
315 316 317 318
        if (KrServices::cmdExist("arj"))
            packer << KrServices::fullPathName("arj") << "-y" << "-v" << "x";
        else
            packer << KrServices::fullPathName("unarj") << "x";
319
    } else if (type == "7z")  packer << find7zExecutable() << "-y" << "x";
320
    else if (type == "rpm") {
321 322
        // TODO use QTemporaryFile (setAutoRemove(false) when asynchrone)
        cpioName = QDir::tempPath() + QStringLiteral("/contents.cpio");
Fathi Boudra's avatar
Fathi Boudra committed
323 324 325 326 327 328

        KrLinecountingProcess cpio;
        cpio << KrServices::fullPathName("rpm2cpio") << archive;
        cpio.setStandardOutputFile(cpioName); // TODO maybe no tmpfile but a pipe (setStandardOutputProcess(packer))
        cpio.start();
        if (!cpio.waitForFinished() || cpio.exitStatus() != QProcess::NormalExit || !checkStatus("cpio", cpio.exitCode())) {
Pino Toscano's avatar
Pino Toscano committed
329
            observer->detailedError(i18n("Failed to convert rpm (%1) to cpio.", archive), cpio.getErrorMsg());
Fathi Boudra's avatar
Fathi Boudra committed
330 331 332 333 334
            return 0;
        }

        archive = cpioName;
        packer << KrServices::fullPathName("cpio") << "--force-local" << "--no-absolute-filenames" <<  "-iuvdF";
335
    } else if (type == "deb") {
336 337
        // TODO use QTemporaryFile (setAutoRemove(false) when asynchrone)
        cpioName = QDir::tempPath() + QStringLiteral("/contents.tar");
Fathi Boudra's avatar
Fathi Boudra committed
338 339 340 341 342

        KrLinecountingProcess dpkg;
        dpkg << KrServices::fullPathName("dpkg") << "--fsys-tarfile" << archive;
        dpkg.setStandardOutputFile(cpioName); // TODO maybe no tmpfile but a pipe (setStandardOutputProcess(packer))
        dpkg.start();
343
        if (!dpkg.waitForFinished() || dpkg.exitStatus() != QProcess::NormalExit || !checkStatus("deb", dpkg.exitCode())) {
Pino Toscano's avatar
Pino Toscano committed
344
            observer->detailedError(i18n("Failed to convert deb (%1) to tar.", archive), dpkg.getErrorMsg());
Fathi Boudra's avatar
Fathi Boudra committed
345 346 347 348 349 350 351 352
            return 0;
        }

        archive = cpioName;
        packer << KrServices::fullPathName("tar") << "xvf";
    } else return false;

    if (!password.isNull()) {
353
        if (type == "zip")
Fathi Boudra's avatar
Fathi Boudra committed
354
            packer << "-P" << password;
355
        if (type == "arj")
Fathi Boudra's avatar
Fathi Boudra committed
356
            packer << QString("-g%1").arg(password);
357
        if (type == "ace" || type == "rar" || type == "7z")
Fathi Boudra's avatar
Fathi Boudra committed
358
            packer << QString("-p%1").arg(password);
359
    }
Fathi Boudra's avatar
Fathi Boudra committed
360 361 362 363

    // unpack the files
    KrLinecountingProcess proc;
    proc << packer << archive;
364
    if (type == "bzip2" || type == "gzip" || type == "lzma" || type == "xz") {
Fathi Boudra's avatar
Fathi Boudra committed
365 366
        QString arcname = archive.mid(archive.lastIndexOf("/") + 1);
        if (arcname.contains(".")) arcname = arcname.left(arcname.lastIndexOf("."));
367
            proc.setStandardOutputFile(dest + '/' + arcname);
368
    }
369
    if (type == "ace" && QFile("/dev/ptmx").exists())    // Don't remove, unace crashes if missing!!!
Fathi Boudra's avatar
Fathi Boudra committed
370 371 372 373 374 375 376
        proc.setStandardInputFile("/dev/ptmx");

    proc.setWorkingDirectory(dest);

    // tell the user to wait
    observer->subJobStarted(i18n("Unpacking File(s)"), count);
    if (count != 0) {
377
        connect(&proc, &KrLinecountingProcess::newOutputLines, observer, &KRarcObserver::incrementProgress);
378
        if (type == "rpm")
379
            connect(&proc, &KrLinecountingProcess::newErrorLines, observer, &KRarcObserver::incrementProgress);
380
    }
Fathi Boudra's avatar
Fathi Boudra committed
381 382 383 384 385

    // start the unpacking process
    proc.start();
    // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking
    // it would be better to connect to started(), error() and finished()
386 387
    if (proc.waitForStarted())
        while (proc.state() == QProcess::Running) {
Fathi Boudra's avatar
Fathi Boudra committed
388 389 390 391 392 393 394 395 396 397 398 399
            observer->processEvents();
            if (observer->wasCancelled())
                proc.kill();
        }
    ; // busy wait - need to find something better...
    observer->subJobStopped();

    if (!cpioName.isEmpty())
        QFile(cpioName).remove();      /* remove the cpio file */

    // check the return value
    if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(type, proc.exitCode())) {
Pino Toscano's avatar
Pino Toscano committed
400
        observer->detailedError(i18n("Failed to unpack %1.", archive),
Fathi Boudra's avatar
Fathi Boudra committed
401 402
                                observer->wasCancelled() ? i18n("User cancelled.") : proc.getErrorMsg());
        return false;
403
    }
Fathi Boudra's avatar
Fathi Boudra committed
404 405
    return true; // SUCCESS
}
406

407
bool KrArcHandler::test(const QString& archive, const QString& type, const QString& password, KRarcObserver *observer, long count)
Fathi Boudra's avatar
Fathi Boudra committed
408 409 410 411 412
{
    // choose the right packer for the job
    QStringList packer;

    // set the right packer to do the job
413 414 415
    if (type == "zip") packer << KrServices::fullPathName("unzip") << "-t";
    else if (type == "tar") packer << KrServices::fullPathName("tar") << "-tvf";
    else if (type == "tgz") packer << KrServices::fullPathName("tar") << "-tvzf";
Fathi Boudra's avatar
Fathi Boudra committed
416
    else if (type == "tarz") packer << KrServices::fullPathName("tar") << "-tvzf";
417 418 419
    else if (type == "tbz") packer << KrServices::fullPathName("tar") << "-tjvf";
    else if (type == "tlz") packer << KrServices::fullPathName("tar") << "--lzma" << "-tvf";
    else if (type == "txz") packer << KrServices::fullPathName("tar") << "--xz" << "-tvf";
Fathi Boudra's avatar
Fathi Boudra committed
420
    else if (type == "gzip") packer << KrServices::fullPathName("gzip") << "-tv";
421
    else if (type == "bzip2") packer << KrServices::fullPathName("bzip2") << "-tv";
Fathi Boudra's avatar
Fathi Boudra committed
422
    else if (type == "lzma") packer << KrServices::fullPathName("lzma") << "-tv";
423 424 425 426 427
    else if (type == "xz")  packer << KrServices::fullPathName("xz") << "-tv";
    else if (type == "rar") packer << KrServices::fullPathName(KrServices::cmdExist("rar") ? "rar" : "unrar") << "t";
    else if (type == "ace") packer << KrServices::fullPathName("unace") << "t";
    else if (type == "lha") packer << KrServices::fullPathName("lha") << "t";
    else if (type == "arj") packer << KrServices::fullPathName(KrServices::cmdExist("arj") ? "arj" : "unarj") << "t";
Fathi Boudra's avatar
Fathi Boudra committed
428
    else if (type == "cpio") packer << KrServices::fullPathName("cpio") << "--only-verify-crc" << "-tvF";
429
    else if (type == "7z")  packer << find7zExecutable() << "-y" << "t";
Fathi Boudra's avatar
Fathi Boudra committed
430 431 432
    else return false;

    if (!password.isNull()) {
433
        if (type == "zip")
Fathi Boudra's avatar
Fathi Boudra committed
434
            packer << "-P" << password;
435
        if (type == "arj")
Fathi Boudra's avatar
Fathi Boudra committed
436
            packer << QString("-g%1").arg(password);
437
        if (type == "ace" || type == "rar" || type == "7z")
Fathi Boudra's avatar
Fathi Boudra committed
438 439
            packer << QString("-p%1").arg(password);
    }
440

Fathi Boudra's avatar
Fathi Boudra committed
441 442 443 444
    // unpack the files
    KrLinecountingProcess proc;
    proc << packer << archive;

445
    if (type == "ace" && QFile("/dev/ptmx").exists())    // Don't remove, unace crashes if missing!!!
Fathi Boudra's avatar
Fathi Boudra committed
446 447 448 449 450
        proc.setStandardInputFile("/dev/ptmx");

    // tell the user to wait
    observer->subJobStarted(i18n("Testing Archive"), count);
    if (count != 0)
451
        connect(&proc, &KrLinecountingProcess::newOutputLines, observer, &KRarcObserver::incrementProgress);
Fathi Boudra's avatar
Fathi Boudra committed
452 453 454 455 456

    // start the unpacking process
    proc.start();
    // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking
    // it would be better to connect to started(), error() and finished()
457 458
    if (proc.waitForStarted())
        while (proc.state() == QProcess::Running) {
Fathi Boudra's avatar
Fathi Boudra committed
459 460 461 462 463 464 465 466 467 468 469 470 471 472
            observer->processEvents();
            if (observer->wasCancelled())
                proc.kill();
        }
    ; // busy wait - need to find something better...
    observer->subJobStopped();

    // check the return value
    if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(type, proc.exitCode()))
        return false;

    return true; // SUCCESS
}

473
bool KrArcHandler::pack(QStringList fileNames, QString type, const QString& dest, long count, QMap<QString, QString> extraProps, KRarcObserver *observer)
Fathi Boudra's avatar
Fathi Boudra committed
474 475 476 477 478
{
    // set the right packer to do the job
    QStringList packer;

    if (type == "zip") {
479
        packer << KrServices::fullPathName("zip") << "-ry";
Davide Gianforte's avatar
Davide Gianforte committed
480
    } else if (type == "cbz") {
481 482
        packer << KrServices::fullPathName("zip") << "-ry";
        type = "zip";
Fathi Boudra's avatar
Fathi Boudra committed
483
    } else if (type == "tar") {
484
        packer << KrServices::fullPathName("tar") << "-cvf";
Fathi Boudra's avatar
Fathi Boudra committed
485
    } else if (type == "tar.gz") {
486 487
        packer << KrServices::fullPathName("tar") << "-cvzf";
        type = "tgz";
Fathi Boudra's avatar
Fathi Boudra committed
488
    } else if (type == "tar.bz2") {
489 490
        packer << KrServices::fullPathName("tar") << "-cvjf";
        type = "tbz";
Fathi Boudra's avatar
Fathi Boudra committed
491
    } else if (type == "tar.lzma") {
492 493
        packer << KrServices::fullPathName("tar") << "--lzma" << "-cvf";
        type = "tlz";
494
    } else if (type == "tar.xz") {
495 496
        packer << KrServices::fullPathName("tar") << "--xz" << "-cvf";
        type = "txz";
Fathi Boudra's avatar
Fathi Boudra committed
497
    } else if (type == "rar") {
498
        packer << KrServices::fullPathName("rar") << "-r" << "a";
Davide Gianforte's avatar
Davide Gianforte committed
499
    } else if (type == "cbr") {
500 501
        packer << KrServices::fullPathName("rar") << "-r" << "a";
        type = "rar";
Fathi Boudra's avatar
Fathi Boudra committed
502
    } else if (type == "lha") {
503
        packer << KrServices::fullPathName("lha") << "a";
Fathi Boudra's avatar
Fathi Boudra committed
504
    } else if (type == "arj") {
505
        packer << KrServices::fullPathName("arj") << "-r" << "-y" << "a";
Fathi Boudra's avatar
Fathi Boudra committed
506
    } else if (type == "7z") {
507
        packer << find7zExecutable() << "-y" << "a";
Fathi Boudra's avatar
Fathi Boudra committed
508 509
    } else return false;

Fathi Boudra's avatar
Fathi Boudra committed
510
    QString password;
Fathi Boudra's avatar
Fathi Boudra committed
511 512 513 514 515

    if (extraProps.count("Password") > 0) {
        password = extraProps[ "Password" ];

        if (!password.isNull()) {
516
            if (type == "zip")
Fathi Boudra's avatar
Fathi Boudra committed
517
                packer << "-P" << password;
518
            else if (type == "arj")
Fathi Boudra's avatar
Fathi Boudra committed
519
                packer << QString("-g%1").arg(password);
520
            else if (type == "ace" || type == "7z")
Fathi Boudra's avatar
Fathi Boudra committed
521
                packer << QString("-p%1").arg(password);
522
            else if (type == "rar") {
Fathi Boudra's avatar
Fathi Boudra committed
523 524 525 526 527
                if (extraProps.count("EncryptHeaders") > 0)
                    packer << QString("-hp%1").arg(password);
                else
                    packer << QString("-p%1").arg(password);
            } else
Fathi Boudra's avatar
Fathi Boudra committed
528
                password.clear();
Fathi Boudra's avatar
Fathi Boudra committed
529
        }
Csaba Karai's avatar
Csaba Karai committed
530
    }
Fathi Boudra's avatar
Fathi Boudra committed
531 532 533 534 535 536

    if (extraProps.count("VolumeSize") > 0) {
        QString sizeStr = extraProps[ "VolumeSize" ];
        KIO::filesize_t size = sizeStr.toLongLong();

        if (size >= 10000) {
537
            if (type == "arj" || type == "rar")
Fathi Boudra's avatar
Fathi Boudra committed
538 539
                packer << QString("-v%1b").arg(sizeStr);
        }
540 541
    }

Fathi Boudra's avatar
Fathi Boudra committed
542 543 544 545 546 547 548
    if (extraProps.count("CompressionLevel") > 0) {
        int level = extraProps[ "CompressionLevel" ].toInt() - 1;
        if (level < 0)
            level = 0;
        if (level > 8)
            level = 8;

549
        if (type == "rar") {
Fathi Boudra's avatar
Fathi Boudra committed
550 551
            static const int rarLevels[] = { 0, 1, 2, 2, 3, 3, 4, 4, 5 };
            packer << QString("-m%1").arg(rarLevels[ level ]);
552
        } else if (type == "arj") {
Fathi Boudra's avatar
Fathi Boudra committed
553 554
            static const int arjLevels[] = { 0, 4, 4, 3, 3, 2, 2, 1, 1 };
            packer << QString("-m%1").arg(arjLevels[ level ]);
555
        } else if (type == "zip") {
Fathi Boudra's avatar
Fathi Boudra committed
556 557
            static const int zipLevels[] = { 0, 1, 2, 4, 5, 6, 7, 8, 9 };
            packer << QString("-%1").arg(zipLevels[ level ]);
558
        } else if (type == "7z") {
Fathi Boudra's avatar
Fathi Boudra committed
559 560 561
            static const int sevenZipLevels[] = { 0, 1, 2, 4, 5, 6, 7, 8, 9 };
            packer << QString("-mx%1").arg(sevenZipLevels[ level ]);
        }
562 563
    }

Fathi Boudra's avatar
Fathi Boudra committed
564 565 566 567 568 569 570
    if (extraProps.count("CommandLineSwitches") > 0)
        packer << QString("%1").arg(extraProps[ "CommandLineSwitches" ]);

    // prepare to pack
    KrLinecountingProcess proc;
    proc << packer << dest;

571 572
    for (auto & fileName : fileNames) {
        proc << fileName;
573 574
    }

Fathi Boudra's avatar
Fathi Boudra committed
575 576 577
    // tell the user to wait
    observer->subJobStarted(i18n("Packing File(s)"), count);
    if (count != 0)
578
        connect(&proc, &KrLinecountingProcess::newOutputLines, observer, &KRarcObserver::incrementProgress);
Fathi Boudra's avatar
Fathi Boudra committed
579 580 581 582 583

    // start the packing process
    proc.start();
    // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking
    // it would be better to connect to started(), error() and finished()
584 585
    if (proc.waitForStarted())
        while (proc.state() == QProcess::Running) {
Fathi Boudra's avatar
Fathi Boudra committed
586 587 588 589 590 591 592 593 594
            observer->processEvents();
            if (observer->wasCancelled())
                proc.kill();
        }
    ; // busy wait - need to find something better...
    observer->subJobStopped();

    // check the return value
    if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(type, proc.exitCode())) {
Pino Toscano's avatar
Pino Toscano committed
595
        observer->detailedError(i18n("Failed to pack %1.", dest),
Fathi Boudra's avatar
Fathi Boudra committed
596 597 598 599 600 601
                                observer->wasCancelled() ? i18n("User cancelled.") : proc.getErrorMsg());
        return false;
    }

    KConfigGroup group(krConfig, "Archives");
    if (group.readEntry("Test Archives", _TestArchives) &&
602
            !test(dest, type, password, observer, count)) {
Pino Toscano's avatar
Pino Toscano committed
603
        observer->error(i18n("Failed to pack %1.", dest));
Fathi Boudra's avatar
Fathi Boudra committed
604
        return false;
605
    }
Fathi Boudra's avatar
Fathi Boudra committed
606
    return true; // SUCCESS
Rafi Yanai's avatar
Rafi Yanai committed
607
}
608

609
bool KrArcHandler::openWallet()
610 611 612 613 614 615 616 617 618
{
    if (!wallet) {
        // find a suitable parent window
        QWidget *actWindow = QApplication::activeWindow();
        if (!actWindow)
            actWindow = (QWidget*) QApplication::desktop();

        wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), actWindow->effectiveWinId());
    }
619
    return (wallet != nullptr);
620 621
}

622
QString KrArcHandler::getPassword(const QString& path)
Fathi Boudra's avatar
Fathi Boudra committed
623 624 625 626 627 628
{
    QString password;

    QString key = "krarc-" + path;

    if (!KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), KWallet::Wallet::PasswordFolder(), key)) {
629
        if (!KWallet::Wallet::isOpen(KWallet::Wallet::NetworkWallet())  && wallet != nullptr) {
Fathi Boudra's avatar
Fathi Boudra committed
630
            delete wallet;
631
            wallet = nullptr;
Fathi Boudra's avatar
Fathi Boudra committed
632
        }
633
        if (openWallet() && wallet->hasFolder(KWallet::Wallet::PasswordFolder())) {
Fathi Boudra's avatar
Fathi Boudra committed
634 635 636 637 638 639 640 641 642 643 644 645
            wallet->setFolder(KWallet::Wallet::PasswordFolder());
            QMap<QString, QString> map;
            if (wallet->readMap(key, map) == 0) {
                QMap<QString, QString>::const_iterator it = map.constFind("password");
                if (it != map.constEnd())
                    password = it.value();
            }
        }
    }

    bool keep = true;
    QString user = "archive";
646
    QPointer<KPasswordDialog> passDlg = new KPasswordDialog(nullptr, KPasswordDialog::ShowKeepPassword);
647 648
            passDlg->setPrompt(i18n("This archive is encrypted, please supply the password:") ),
            passDlg->setUsername(user);
649
    passDlg->setPassword(password);
650
    if (passDlg->exec() == KPasswordDialog::Accepted) {
651
        password = passDlg->password();
Fathi Boudra's avatar
Fathi Boudra committed
652
        if (keep) {
653
            if (!KWallet::Wallet::isOpen(KWallet::Wallet::NetworkWallet()) && wallet != nullptr) {
Fathi Boudra's avatar
Fathi Boudra committed
654
                delete wallet;
655
                wallet = nullptr;
Fathi Boudra's avatar
Fathi Boudra committed
656
            }
657
            if (openWallet()) {
Fathi Boudra's avatar
Fathi Boudra committed
658 659 660 661 662 663 664 665 666 667 668 669
                bool ok = true;
                if (!wallet->hasFolder(KWallet::Wallet::PasswordFolder()))
                    ok = wallet->createFolder(KWallet::Wallet::PasswordFolder());
                if (ok) {
                    wallet->setFolder(KWallet::Wallet::PasswordFolder());
                    QMap<QString, QString> map;
                    map.insert("login", "archive");
                    map.insert("password", password);
                    wallet->writeMap(key, map);
                }
            }
        }
Csaba Karai's avatar
Csaba Karai committed
670
        delete passDlg;
Fathi Boudra's avatar
Fathi Boudra committed
671 672
        return password;
    }
673
    delete passDlg;
Fathi Boudra's avatar
Fathi Boudra committed
674 675

    return "";
Rafi Yanai's avatar
Rafi Yanai committed
676
}
677

678
bool KrArcHandler::isArchive(const QUrl &url)
Fathi Boudra's avatar
Fathi Boudra committed
679
{
680
    QString protocol = url.scheme();
Fathi Boudra's avatar
Fathi Boudra committed
681 682 683
    if (arcProtocols.indexOf(protocol) != -1)
        return true;
    else return false;
Rafi Yanai's avatar
Rafi Yanai committed
684
}
685

686
QString KrArcHandler::getType(bool &encrypted, QString fileName, const QString& mime, bool checkEncrypted, bool fast)
Fathi Boudra's avatar
Fathi Boudra committed
687
{
688
    QString result = detectArchive(encrypted, std::move(fileName), checkEncrypted, fast);
689
    if (result.isNull()) {
690 691
        // Then the type is based on the mime type
        return getShortTypeFromMime(mime);
692
    }
693
    return result;
Fathi Boudra's avatar
Fathi Boudra committed
694 695
}

696
bool KrArcHandler::checkStatus(const QString& type, int exitCode)
Fathi Boudra's avatar
Fathi Boudra committed
697
{
698
    return KrArcBaseManager::checkStatus(type, exitCode);
Rafi Yanai's avatar
Rafi Yanai committed
699
}
700

701
void KrArcHandler::checkIf7zIsEncrypted(bool &encrypted, QString fileName)
Fathi Boudra's avatar
Fathi Boudra committed
702
{
703 704 705
    // Reminder: If that function is modified, it's important to research if the
    // changes must also be applied to `kio_krarcProtocol::checkIf7zIsEncrypted()`

706 707
    Kr7zEncryptionChecker proc;
    // TODO incorporate all this in Kr7zEncryptionChecker
708
    proc << find7zExecutable() << "-y" << "t";
709 710 711 712
    proc << fileName;
    proc.start();
    proc.waitForFinished();
    encrypted = proc.isEncrypted();
Rafi Yanai's avatar
Rafi Yanai committed
713 714
}

715
QString KrArcHandler::registeredProtocol(const QString& mimetype)
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
{
    if (slaveMap == nullptr) {
        slaveMap = new QMap<QString, QString>();

        KConfigGroup group(krConfig, "Protocols");
        QStringList protList = group.readEntry("Handled Protocols", QStringList());
        for (auto & it : protList) {
            QStringList mimes = group.readEntry(QString("Mimes For %1").arg(it), QStringList());
            for (auto & mime : mimes)
                (*slaveMap)[mime] = it;
        }
    }
    QString protocol = (*slaveMap)[mimetype];
    if (protocol.isEmpty()) {
        if (krarcArchiveMimetypes.contains(mimetype)) {
            return QStringLiteral("krarc");
        }
        protocol = KProtocolManager::protocolForArchiveMimetype(mimetype);
    }
    return protocol;
}

738
void KrArcHandler::clearProtocolCache()
739 740 741 742 743 744
{
    if (slaveMap)
        delete slaveMap;
    slaveMap = nullptr;
}