krarchandler.cpp 28.4 KB
Newer Older
Rafi Yanai's avatar
Rafi Yanai committed
1
/***************************************************************************
2 3 4 5
                                krarchandler.cpp
                            -------------------
   copyright            : (C) 2001 by Shie Erlich & Rafi Yanai
   email                : krusader@users.sourceforge.net
Fathi Boudra's avatar
Fathi Boudra committed
6
   web site   : http://krusader.sourceforge.net
7
---------------------------------------------------------------------------
8
 Description
9 10
***************************************************************************

11
 A
12

13 14 15 16 17 18
    db   dD d8888b. db    db .d8888.  .d8b.  d8888b. d88888b d8888b.
    88 ,8P' 88  `8D 88    88 88'  YP d8' `8b 88  `8D 88'     88  `8D
    88,8P   88oobY' 88    88 `8bo.   88ooo88 88   88 88ooooo 88oobY'
    88`8b   88`8b   88    88   `Y8b. 88~~~88 88   88 88~~~~~ 88`8b
    88 `88. 88 `88. 88b  d88 db   8D 88   88 88  .8D 88.     88 `88.
    YP   YD 88   YD ~Y8888P' `8888Y' YP   YP Y8888D' Y88888P 88   YD
19

20
                                                    S o u r c e    F i l e
21 22 23 24 25 26 27 28

***************************************************************************
*                                                                         *
*   This program 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.                                   *
*                                                                         *
Fathi Boudra's avatar
Fathi Boudra committed
29
***************************************************************************/
Fathi Boudra's avatar
Fathi Boudra committed
30 31 32

#include "krarchandler.h"

Simon Persson's avatar
Simon Persson committed
33
#include <QtCore/QDebug>
34
#include <QtCore/QDir>
Fathi Boudra's avatar
Fathi Boudra committed
35
#include <QtCore/QFile>
36
#include <QtWidgets/QApplication>
Fathi Boudra's avatar
Fathi Boudra committed
37

38
#include <KArchive/KTar>
39
#include <KConfigCore/KSharedConfig>
Simon Persson's avatar
Simon Persson committed
40
#include <KI18n/KLocalizedString>
41
#include <KIO/Global>
42
#include <KWallet/KWallet>
43
#include <KWidgetsAddons/KMessageBox>
44
#include <KWidgetsAddons/KPasswordDialog>
Fathi Boudra's avatar
Fathi Boudra committed
45

46
#include "../krglobal.h"
Rafi Yanai's avatar
Rafi Yanai committed
47
#include "../defaults.h"
48
#include "../krservices.h"
49
#include "../Dialogs/krpleasewait.h"
Rafi Yanai's avatar
Rafi Yanai committed
50

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

58
    virtual void processEvents() Q_DECL_OVERRIDE {
Fathi Boudra's avatar
Fathi Boudra committed
59 60 61 62
        usleep(1000);
        qApp->processEvents();
    }

63
    virtual void subJobStarted(const QString & jobTitle, int count) Q_DECL_OVERRIDE {
Fathi Boudra's avatar
Fathi Boudra committed
64 65 66
        krApp->startWaiting(jobTitle, count, true);
    }

67
    virtual void subJobStopped() Q_DECL_OVERRIDE {
Fathi Boudra's avatar
Fathi Boudra committed
68 69 70
        krApp->stopWait();
    }

71
    virtual bool wasCancelled() Q_DECL_OVERRIDE {
Fathi Boudra's avatar
Fathi Boudra committed
72 73 74
        return krApp->wasWaitingCancelled();
    }

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

79
    virtual void detailedError(const QString & error, const QString & details) Q_DECL_OVERRIDE {
Fathi Boudra's avatar
Fathi Boudra committed
80 81 82
        KMessageBox::detailedError(krApp, error, details, i18n("Error"));
    }

83
    virtual void incrementProgress(int c) Q_DECL_OVERRIDE {
Fathi Boudra's avatar
Fathi Boudra committed
84 85
        krApp->plzWait->incProgress(c);
    }
86
};
87
#endif
88

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

KWallet::Wallet * KRarcHandler::wallet = 0;
92

Fathi Boudra's avatar
Fathi Boudra committed
93 94 95 96 97 98 99 100 101
QStringList KRarcHandler::supportedPackers()
{
    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");
102
    if (KrServices::cmdExist("xz")) packers.append("xz");
Fathi Boudra's avatar
Fathi Boudra committed
103 104 105 106 107 108 109 110 111 112 113 114
    if (KrServices::cmdExist("unzip")) packers.append("unzip");
    if (KrServices::cmdExist("zip")) packers.append("zip");
    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");
    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");
Simon Persson's avatar
Simon Persson committed
115
    // qDebug() << "Supported Packers:" << endl;
Fathi Boudra's avatar
Fathi Boudra committed
116 117
    //QStringList::Iterator it;
    //for( it = packers.begin(); it != packers.end(); ++it )
Simon Persson's avatar
Simon Persson committed
118
    // qDebug() << *it << endl;
Fathi Boudra's avatar
Fathi Boudra committed
119 120 121 122 123 124 125 126 127 128

    return packers;
}

bool KRarcHandler::arcSupported(QString type)
{
    // lst will contain the supported unpacker list...
    KConfigGroup group(krConfig, "Archives");
    QStringList lst = group.readEntry("Supported Packers", QStringList());

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
    // Normalize the type of the file
    if (type.length() > 4) {
        // 7zip files are a not a normal case because their mimetype does not
        // follow the norm of other types: zip, tar, lha, ace, arj, etc.
        if (type == "application/x-7z-compressed")
            type = "-7z";
        else
            // If it's a rar file but its mimetype isn't "application/x-rar"
            if (type == "application/x-rar-compressed")
                type = "-rar";
            else
                type = type.right(4);
    }

    if ((type == "-zip" || type == "/zip") && lst.contains("unzip"))
Fathi Boudra's avatar
Fathi Boudra committed
144 145 146 147 148 149 150 151 152
        return true;
    else if (type == "-tar" && lst.contains("tar"))
        return true;
    else if (type == "-tbz" && lst.contains("tar"))
        return true;
    else if (type == "-tgz" && lst.contains("tar"))
        return true;
    else if (type == "-tlz" && lst.contains("tar"))
        return true;
153 154
    else if (type == "-txz" && lst.contains("tar"))
        return true;
Fathi Boudra's avatar
Fathi Boudra committed
155 156 157 158 159 160 161 162
    else if (type == "tarz" && lst.contains("tar"))
        return true;
    else if (type == "gzip" && lst.contains("gzip"))
        return true;
    else if (type == "zip2" && lst.contains("bzip2"))
        return true;
    else if (type == "lzma" && lst.contains("lzma"))
        return true;
163 164
    else if (type == "-xz" && lst.contains("xz"))
        return true;
Fathi Boudra's avatar
Fathi Boudra committed
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
    else if (type == "-lha" && lst.contains("lha"))
        return true;
    else if (type == "-ace" && lst.contains("unace"))
        return true;
    else if (type == "-rpm" && lst.contains("cpio"))
        return true;
    else if (type == "cpio" && lst.contains("cpio"))
        return true;
    else if (type == "-rar" && (lst.contains("unrar") || lst.contains("rar")))
        return true;
    else if (type == "-arj" && (lst.contains("unarj") || lst.contains("arj")))
        return true;
    else if (type == "-deb" && (lst.contains("dpkg") && lst.contains("tar")))
        return true;
    else if (type == "-7z" && lst.contains("7z"))
        return true;
    // not supported : (
182
    return false;
Fathi Boudra's avatar
Fathi Boudra committed
183 184 185 186 187 188 189 190 191 192
}

long KRarcHandler::arcFileCount(QString archive, QString type, QString password, KRarcObserver *observer)
{
    int divideWith = 1;

    // first check if supported
    if (!arcSupported(type)) return 0;

    // bzip an gzip archive contains only one file
193
    if (type == "zip2" || type == "gzip" || type == "lzma" || type == "-xz") return 1L;
Fathi Boudra's avatar
Fathi Boudra committed
194 195 196 197 198 199 200 201 202 203

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

    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";
    else if (type == "tarz") lister << KrServices::fullPathName("tar") << "-tvzf";
    else if (type == "-tbz") lister << KrServices::fullPathName("tar") << "-tjvf";
    else if (type == "-tlz") lister << KrServices::fullPathName("tar") << "--lzma" << "-tvf";
204
    else if (type == "-txz") lister << KrServices::fullPathName("tar") << "--xz" << "-tvf";
Fathi Boudra's avatar
Fathi Boudra committed
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
    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") {
        if (KrServices::cmdExist("arj"))
            lister << KrServices::fullPathName("arj") << "v" << "-y" << "-v",
            divideWith = 4;
        else
            lister << KrServices::fullPathName("unarj") << "l";
    } else if (type == "-rpm") lister << KrServices::fullPathName("rpm") << "--dump" << "-lpq";
    else if (type == "-deb") lister << KrServices::fullPathName("dpkg") << "-c";
    else if (type == "-7z")  lister << KrServices::fullPathName("7z") << "-y" << "l";
    else return 0L;

    if (!password.isNull()) {
        if (type == "-arj")
            lister << QString("-g%1").arg(password);
        if (type == "-ace" || type == "-rar" || type == "-7z")
            lister << QString("-p%1").arg(password);
224
    }
Fathi Boudra's avatar
Fathi Boudra committed
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248

    // 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;
    if (type == "-ace" && QFile("/dev/ptmx").exists())     // Don't remove, unace crashes if missing!!!
        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
249
        observer->detailedError(i18n("Failed to list the content of the archive (%1).", archive),
Fathi Boudra's avatar
Fathi Boudra committed
250 251
                                QString::fromLocal8Bit(list.readAllStandardError()));
        return 0;
252 253
    }

Fathi Boudra's avatar
Fathi Boudra committed
254 255 256 257 258 259 260 261 262 263 264 265 266
    count = list.readAllStandardOutput().count('\n');

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

    return count / divideWith;
}

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

    // count the files in the archive
    long count = arcFileCount(archive, type, password, observer);
    if (count == 0) return false;   // not supported
    if (count == 1) count = 0 ;

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

    // set the right packer to do the job
    if (type == "-zip") packer << KrServices::fullPathName("unzip") << "-o" ;
    else if (type == "-tar") packer << KrServices::fullPathName("tar") << "-xvf";
    else if (type == "-tgz") packer << KrServices::fullPathName("tar") << "-xvzf";
    else if (type == "tarz") packer << KrServices::fullPathName("tar") << "-xvzf";
    else if (type == "-tbz") packer << KrServices::fullPathName("tar") << "-xjvf";
    else if (type == "-tlz") packer << KrServices::fullPathName("tar") << "--lzma" << "-xvf";
289
    else if (type == "-txz") packer << KrServices::fullPathName("tar") << "--xz" << "-xvf";
Fathi Boudra's avatar
Fathi Boudra committed
290 291 292
    else if (type == "gzip") packer << KrServices::fullPathName("gzip") << "-cd";
    else if (type == "zip2") packer << KrServices::fullPathName("bzip2") << "-cdk";
    else if (type == "lzma") packer << KrServices::fullPathName("lzma") << "-cdk";
293
    else if (type == "-xz")  packer << KrServices::fullPathName("xz") << "-cdk";
Fathi Boudra's avatar
Fathi Boudra committed
294 295 296 297 298 299 300 301 302 303
    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") {
        if (KrServices::cmdExist("arj"))
            packer << KrServices::fullPathName("arj") << "-y" << "-v" << "x";
        else
            packer << KrServices::fullPathName("unarj") << "x";
    } else if (type == "-7z")  packer << KrServices::fullPathName("7z") << "-y" << "x";
    else if (type == "-rpm") {
304 305
        // TODO use QTemporaryFile (setAutoRemove(false) when asynchrone)
        cpioName = QDir::tempPath() + QStringLiteral("/contents.cpio");
Fathi Boudra's avatar
Fathi Boudra committed
306 307 308 309 310 311

        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
312
            observer->detailedError(i18n("Failed to convert rpm (%1) to cpio.", archive), cpio.getErrorMsg());
Fathi Boudra's avatar
Fathi Boudra committed
313 314 315 316 317 318
            return 0;
        }

        archive = cpioName;
        packer << KrServices::fullPathName("cpio") << "--force-local" << "--no-absolute-filenames" <<  "-iuvdF";
    } else if (type == "-deb") {
319 320
        // TODO use QTemporaryFile (setAutoRemove(false) when asynchrone)
        cpioName = QDir::tempPath() + QStringLiteral("/contents.tar");
Fathi Boudra's avatar
Fathi Boudra committed
321 322 323 324 325 326

        KrLinecountingProcess dpkg;
        dpkg << KrServices::fullPathName("dpkg") << "--fsys-tarfile" << archive;
        dpkg.setStandardOutputFile(cpioName); // TODO maybe no tmpfile but a pipe (setStandardOutputProcess(packer))
        dpkg.start();
        if (!dpkg.waitForFinished() || dpkg.exitStatus() != QProcess::NormalExit || !checkStatus("-deb", dpkg.exitCode())) {
Pino Toscano's avatar
Pino Toscano committed
327
            observer->detailedError(i18n("Failed to convert deb (%1) to tar.", archive), dpkg.getErrorMsg());
Fathi Boudra's avatar
Fathi Boudra committed
328 329 330 331 332 333 334 335 336 337 338 339 340 341
            return 0;
        }

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

    if (!password.isNull()) {
        if (type == "-zip")
            packer << "-P" << password;
        if (type == "-arj")
            packer << QString("-g%1").arg(password);
        if (type == "-ace" || type == "-rar" || type == "-7z")
            packer << QString("-p%1").arg(password);
342
    }
Fathi Boudra's avatar
Fathi Boudra committed
343 344 345 346

    // unpack the files
    KrLinecountingProcess proc;
    proc << packer << archive;
347
    if (type == "zip2" || type == "gzip" || type == "lzma" || type == "-xz") {
Fathi Boudra's avatar
Fathi Boudra committed
348 349 350
        QString arcname = archive.mid(archive.lastIndexOf("/") + 1);
        if (arcname.contains(".")) arcname = arcname.left(arcname.lastIndexOf("."));
        proc.setStandardOutputFile(dest + '/' + arcname);
351
    }
Fathi Boudra's avatar
Fathi Boudra committed
352 353 354 355 356 357 358 359 360 361 362 363 364
    if (type == "-ace" && QFile("/dev/ptmx").exists())    // Don't remove, unace crashes if missing!!!
        proc.setStandardInputFile("/dev/ptmx");

    proc.setWorkingDirectory(dest);

    // tell the user to wait
    observer->subJobStarted(i18n("Unpacking File(s)"), count);
    if (count != 0) {
        connect(&proc, SIGNAL(newOutputLines(int)),
                observer, SLOT(incrementProgress(int)));
        if (type == "-rpm")
            connect(&proc, SIGNAL(newErrorLines(int)),
                    observer, SLOT(incrementProgress(int)));
365
    }
Fathi Boudra's avatar
Fathi Boudra committed
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383

    // 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()
    if (proc.waitForStarted()) while (proc.state() == QProcess::Running) {
            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
384
        observer->detailedError(i18n("Failed to unpack %1.", archive),
Fathi Boudra's avatar
Fathi Boudra committed
385 386
                                observer->wasCancelled() ? i18n("User cancelled.") : proc.getErrorMsg());
        return false;
387
    }
Fathi Boudra's avatar
Fathi Boudra committed
388 389
    return true; // SUCCESS
}
390

391
bool KRarcHandler::test(QString archive, QString type, QString password, KRarcObserver *observer, long count)
Fathi Boudra's avatar
Fathi Boudra committed
392 393 394 395 396 397 398 399 400 401 402
{
    // choose the right packer for the job
    QStringList packer;

    // set the right packer to do the job
    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";
    else if (type == "tarz") packer << KrServices::fullPathName("tar") << "-tvzf";
    else if (type == "-tbz") packer << KrServices::fullPathName("tar") << "-tjvf";
    else if (type == "-tlz") packer << KrServices::fullPathName("tar") << "--lzma" << "-tvf";
403
    else if (type == "-txz") packer << KrServices::fullPathName("tar") << "--xz" << "-tvf";
Fathi Boudra's avatar
Fathi Boudra committed
404 405 406
    else if (type == "gzip") packer << KrServices::fullPathName("gzip") << "-tv";
    else if (type == "zip2") packer << KrServices::fullPathName("bzip2") << "-tv";
    else if (type == "lzma") packer << KrServices::fullPathName("lzma") << "-tv";
407
    else if (type == "-xz")  packer << KrServices::fullPathName("xz") << "-tv";
Fathi Boudra's avatar
Fathi Boudra committed
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
    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";
    else if (type == "cpio") packer << KrServices::fullPathName("cpio") << "--only-verify-crc" << "-tvF";
    else if (type == "-7z")  packer << KrServices::fullPathName("7z") << "-y" << "t";
    else return false;

    if (!password.isNull()) {
        if (type == "-zip")
            packer << "-P" << password;
        if (type == "-arj")
            packer << QString("-g%1").arg(password);
        if (type == "-ace" || type == "-rar" || type == "-7z")
            packer << QString("-p%1").arg(password);
    }
424

Fathi Boudra's avatar
Fathi Boudra committed
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
    // unpack the files
    KrLinecountingProcess proc;
    proc << packer << archive;

    if (type == "-ace" && QFile("/dev/ptmx").exists())    // Don't remove, unace crashes if missing!!!
        proc.setStandardInputFile("/dev/ptmx");

    // tell the user to wait
    observer->subJobStarted(i18n("Testing Archive"), count);
    if (count != 0)
        connect(&proc, SIGNAL(newOutputLines(int)),
                observer, SLOT(incrementProgress(int)));

    // 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()
    if (proc.waitForStarted()) while (proc.state() == QProcess::Running) {
            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
}

bool KRarcHandler::pack(QStringList fileNames, QString type, QString dest, long count, QMap<QString, QString> extraProps, KRarcObserver *observer)
{
    // set the right packer to do the job
    QStringList packer;

    if (type == "zip") {
        packer << KrServices::fullPathName("zip") << "-ry"; type = "-zip";
    } else if (type == "tar") {
        packer << KrServices::fullPathName("tar") << "-cvf"; type = "-tar";
    } else if (type == "tar.gz") {
        packer << KrServices::fullPathName("tar") << "-cvzf"; type = "-tgz";
    } else if (type == "tar.bz2") {
        packer << KrServices::fullPathName("tar") << "-cvjf"; type = "-tbz";
    } else if (type == "tar.lzma") {
        packer << KrServices::fullPathName("tar") << "--lzma" << "-cvf"; type = "-tlz";
472 473
    } else if (type == "tar.xz") {
        packer << KrServices::fullPathName("tar") << "--xz" << "-cvf"; type = "-txz";
Fathi Boudra's avatar
Fathi Boudra committed
474 475 476 477 478 479 480 481 482 483
    } else if (type == "rar") {
        packer << KrServices::fullPathName("rar") << "-r" << "a"; type = "-rar";
    } else if (type == "lha") {
        packer << KrServices::fullPathName("lha") << "a"; type = "-lha";
    } else if (type == "arj") {
        packer << KrServices::fullPathName("arj") << "-r" << "-y" << "a"; type = "-arj";
    } else if (type == "7z") {
        packer << KrServices::fullPathName("7z") << "-y" << "a"; type = "-7z";
    } else return false;

Fathi Boudra's avatar
Fathi Boudra committed
484
    QString password;
Fathi Boudra's avatar
Fathi Boudra committed
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501

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

        if (!password.isNull()) {
            if (type == "-zip")
                packer << "-P" << password;
            else if (type == "-arj")
                packer << QString("-g%1").arg(password);
            else if (type == "-ace" || type == "-7z")
                packer << QString("-p%1").arg(password);
            else if (type == "-rar") {
                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
502
                password.clear();
Fathi Boudra's avatar
Fathi Boudra committed
503
        }
Csaba Karai's avatar
Csaba Karai committed
504
    }
Fathi Boudra's avatar
Fathi Boudra committed
505 506 507 508 509 510 511 512 513

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

        if (size >= 10000) {
            if (type == "-arj" || type == "-rar")
                packer << QString("-v%1b").arg(sizeStr);
        }
514 515
    }

Fathi Boudra's avatar
Fathi Boudra committed
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
    if (extraProps.count("CompressionLevel") > 0) {
        int level = extraProps[ "CompressionLevel" ].toInt() - 1;
        if (level < 0)
            level = 0;
        if (level > 8)
            level = 8;

        if (type == "-rar") {
            static const int rarLevels[] = { 0, 1, 2, 2, 3, 3, 4, 4, 5 };
            packer << QString("-m%1").arg(rarLevels[ level ]);
        } else if (type == "-arj") {
            static const int arjLevels[] = { 0, 4, 4, 3, 3, 2, 2, 1, 1 };
            packer << QString("-m%1").arg(arjLevels[ level ]);
        } else if (type == "-zip") {
            static const int zipLevels[] = { 0, 1, 2, 4, 5, 6, 7, 8, 9 };
            packer << QString("-%1").arg(zipLevels[ level ]);
        } else if (type == "-7z") {
            static const int sevenZipLevels[] = { 0, 1, 2, 4, 5, 6, 7, 8, 9 };
            packer << QString("-mx%1").arg(sevenZipLevels[ level ]);
        }
536 537
    }

Fathi Boudra's avatar
Fathi Boudra committed
538 539 540 541 542 543 544 545 546
    if (extraProps.count("CommandLineSwitches") > 0)
        packer << QString("%1").arg(extraProps[ "CommandLineSwitches" ]);

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

    for (QStringList::Iterator file = fileNames.begin(); file != fileNames.end(); ++file) {
        proc << *file;
547 548
    }

Fathi Boudra's avatar
Fathi Boudra committed
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
    // tell the user to wait
    observer->subJobStarted(i18n("Packing File(s)"), count);
    if (count != 0)
        connect(&proc, SIGNAL(newOutputLines(int)),
                observer, SLOT(incrementProgress(int)));

    // 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()
    if (proc.waitForStarted()) while (proc.state() == QProcess::Running) {
            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
569
        observer->detailedError(i18n("Failed to pack %1.", dest),
Fathi Boudra's avatar
Fathi Boudra committed
570 571 572 573 574 575
                                observer->wasCancelled() ? i18n("User cancelled.") : proc.getErrorMsg());
        return false;
    }

    KConfigGroup group(krConfig, "Archives");
    if (group.readEntry("Test Archives", _TestArchives) &&
576
            !test(dest, type, password, observer, count)) {
Pino Toscano's avatar
Pino Toscano committed
577
        observer->error(i18n("Failed to pack %1.", dest));
Fathi Boudra's avatar
Fathi Boudra committed
578
        return false;
579
    }
Fathi Boudra's avatar
Fathi Boudra committed
580
    return true; // SUCCESS
Rafi Yanai's avatar
Rafi Yanai committed
581
}
582

583 584 585 586 587 588 589 590 591 592 593 594 595
bool KRarcHandler::openWallet()
{
    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());
    }
    return (wallet != 0);
}

Fathi Boudra's avatar
Fathi Boudra committed
596 597 598 599 600 601 602 603 604 605 606
QString KRarcHandler::getPassword(QString path)
{
    QString password;

    QString key = "krarc-" + path;

    if (!KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), KWallet::Wallet::PasswordFolder(), key)) {
        if (!KWallet::Wallet::isOpen(KWallet::Wallet::NetworkWallet())  && wallet != 0) {
            delete wallet;
            wallet = 0;
        }
607
        if (openWallet() && wallet->hasFolder(KWallet::Wallet::PasswordFolder())) {
Fathi Boudra's avatar
Fathi Boudra committed
608 609 610 611 612 613 614 615 616 617 618 619
            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";
620 621 622
    QPointer<KPasswordDialog> passDlg = new KPasswordDialog(0L, KPasswordDialog::ShowKeepPassword);
            passDlg->setPrompt(i18n("This archive is encrypted, please supply the password:") ),
            passDlg->setUsername(user);
623
    passDlg->setPassword(password);
624
    if (passDlg->exec() == KPasswordDialog::Accepted) {
625
        password = passDlg->password();
Fathi Boudra's avatar
Fathi Boudra committed
626 627 628 629 630
        if (keep) {
            if (!KWallet::Wallet::isOpen(KWallet::Wallet::NetworkWallet()) && wallet != 0) {
                delete wallet;
                wallet = 0;
            }
631
            if (openWallet()) {
Fathi Boudra's avatar
Fathi Boudra committed
632 633 634 635 636 637 638 639 640 641 642 643
                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
644
        delete passDlg;
Fathi Boudra's avatar
Fathi Boudra committed
645 646
        return password;
    }
647
    delete passDlg;
Fathi Boudra's avatar
Fathi Boudra committed
648 649

    return "";
Rafi Yanai's avatar
Rafi Yanai committed
650
}
651

652
bool KRarcHandler::isArchive(const QUrl &url)
Fathi Boudra's avatar
Fathi Boudra committed
653
{
654
    QString protocol = url.scheme();
Fathi Boudra's avatar
Fathi Boudra committed
655 656 657
    if (arcProtocols.indexOf(protocol) != -1)
        return true;
    else return false;
Rafi Yanai's avatar
Rafi Yanai committed
658
}
659

660
QString KRarcHandler::getType(bool &encrypted, QString fileName, QString mime, bool checkEncrypted, bool fast)
Fathi Boudra's avatar
Fathi Boudra committed
661
{
662
    QString result = detectArchive(encrypted, fileName, checkEncrypted, fast);
663
    if (result.isNull()) {
Fathi Boudra's avatar
Fathi Boudra committed
664
        result = mime;
665
    } else {
Fathi Boudra's avatar
Fathi Boudra committed
666
        result = '-' + result;
667
    }
Fathi Boudra's avatar
Fathi Boudra committed
668

669
    if (result.endsWith(QLatin1String("-7z"))) {
Fathi Boudra's avatar
Fathi Boudra committed
670
        result = "-7z";
671
    }
672

673 674 675 676
    if (result.endsWith(QLatin1String("-xz"))) {
        result = "-xz";
    }

Fathi Boudra's avatar
Fathi Boudra committed
677 678 679 680 681 682
    return result.right(4);
}


bool KRarcHandler::checkStatus(QString type, int exitCode)
{
683
    // if this code is changed, the code of kio_krarcProtocol::checkStatus() must be reviewed
Fathi Boudra's avatar
Fathi Boudra committed
684 685 686 687
    if (type == "-zip" || type == "-rar" || type == "-7z")
        return exitCode == 0 || exitCode == 1;
    else if (type == "-ace" || type == "zip2" || type == "-lha" || type == "-rpm" || type == "cpio" ||
             type == "-tar" || type == "tarz" || type == "-tbz" || type == "-tgz" || type == "-arj" ||
688
             type == "-deb" || type == "-tlz" || type == "-txz")
Fathi Boudra's avatar
Fathi Boudra committed
689
        return exitCode == 0;
690
    else if (type == "gzip" || type == "lzma" || type == "-xz")
Fathi Boudra's avatar
Fathi Boudra committed
691 692 693
        return exitCode == 0 || exitCode == 2;
    else
        return exitCode == 0;
Rafi Yanai's avatar
Rafi Yanai committed
694
}
695

696
void KRarcHandler::checkIf7zIsEncrypted(bool &encrypted, QString fileName)
Fathi Boudra's avatar
Fathi Boudra committed
697
{
698 699 700 701 702 703 704
    Kr7zEncryptionChecker proc;
    // TODO incorporate all this in Kr7zEncryptionChecker
    proc << KrServices::fullPathName("7z") << "-y" << "t";
    proc << fileName;
    proc.start();
    proc.waitForFinished();
    encrypted = proc.isEncrypted();
Rafi Yanai's avatar
Rafi Yanai committed
705 706
}