jobs.cpp 9.41 KB
Newer Older
1 2
/*
 * Copyright (c) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
3
 * Copyright (c) 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
4
 * Copyright (c) 2009-2012 Raphael Kubo da Costa <rakuco@FreeBSD.org>
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
27

28
#include "jobs.h"
29
#include "ark_debug.h"
30

31
#include <QFileInfo>
32
#include <QThread>
33

34
#include <KLocalizedString>
35

36 37
//#define DEBUG_RACECONDITION

38 39
namespace Kerfuffle
{
40

41 42 43
class Job::Private : public QThread
{
public:
44 45 46 47
    Private(Job *job, QObject *parent = 0)
        : QThread(parent)
        , q(job)
    {
Laurent Montel's avatar
Laurent Montel committed
48
        connect(q, &KJob::result, this, &QThread::quit);
49 50
    }

51
    virtual void run() Q_DECL_OVERRIDE;
52

53
private:
54 55 56 57 58
    Job *q;
};

void Job::Private::run()
{
59
    q->doWork();
60

61 62 63
    if (q->isRunning()) {
        exec();
    }
64 65 66 67 68 69

#ifdef DEBUG_RACECONDITION
    QThread::sleep(2);
#endif
}

70
Job::Job(ReadOnlyArchiveInterface *interface, QObject *parent)
71
    : KJob(parent)
72
    , m_archiveInterface(interface)
73 74
    , m_isRunning(false)
    , d(new Private(this))
75 76 77 78 79 80 81 82 83 84 85 86
{
    static bool onlyOnce = false;
    if (!onlyOnce) {
        qRegisterMetaType<QPair<QString, QString> >("QPair<QString,QString>");
        onlyOnce = true;
    }

    setCapabilities(KJob::Killable);
}

Job::~Job()
{
87 88
    if (d->isRunning()) {
        d->wait();
89 90
    }

91
    delete d;
92
}
93

94 95 96 97 98
ReadOnlyArchiveInterface *Job::archiveInterface()
{
    return m_archiveInterface;
}

99 100 101 102 103
bool Job::isRunning() const
{
    return m_isRunning;
}

104 105
void Job::start()
{
106
    jobTimer.start();
107
    m_isRunning = true;
108
    d->start();
109 110
}

111 112 113 114 115 116
void Job::emitResult()
{
    m_isRunning = false;
    KJob::emitResult();
}

117 118
void Job::connectToArchiveInterfaceSignals()
{
Laurent Montel's avatar
Laurent Montel committed
119 120 121 122 123 124 125 126
    connect(archiveInterface(), &ReadOnlyArchiveInterface::cancelled, this, &Job::onCancelled);
    connect(archiveInterface(), &ReadOnlyArchiveInterface::error, this, &Job::onError);
    connect(archiveInterface(), &ReadOnlyArchiveInterface::entry, this, &Job::onEntry);
    connect(archiveInterface(), &ReadOnlyArchiveInterface::entryRemoved, this, &Job::onEntryRemoved);
    connect(archiveInterface(), &ReadOnlyArchiveInterface::progress, this, &Job::onProgress);
    connect(archiveInterface(), &ReadOnlyArchiveInterface::info, this, &Job::onInfo);
    connect(archiveInterface(), &ReadOnlyArchiveInterface::finished, this, &Job::onFinished, Qt::DirectConnection);
    connect(archiveInterface(), &ReadOnlyArchiveInterface::userQuery, this, &Job::onUserQuery);
127 128
}

129 130
void Job::onCancelled()
{
131
    qCDebug(ARK) << "Cancelled emitted";
132 133 134
    setError(KJob::KilledJobError);
}

135 136
void Job::onError(const QString & message, const QString & details)
{
137
    Q_UNUSED(details)
138

139
    qCDebug(ARK) << "Error emitted:" << message;
140
    setError(KJob::UserDefinedError);
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
    setErrorText(message);
}

void Job::onEntry(const ArchiveEntry & archiveEntry)
{
    emit newEntry(archiveEntry);
}

void Job::onProgress(double value)
{
    setPercent(static_cast<unsigned long>(100.0*value));
}

void Job::onInfo(const QString& info)
{
    emit infoMessage(this, info);
}

void Job::onEntryRemoved(const QString & path)
{
    emit entryRemoved(path);
}

void Job::onFinished(bool result)
{
166
    qCDebug(ARK) << "Job finished, result:" << result << ", time:" << jobTimer.elapsed() << "ms";
167

168
    archiveInterface()->disconnect(this);
169 170 171 172 173 174 175 176 177 178 179

    emitResult();
}

void Job::onUserQuery(Query *query)
{
    emit userQuery(query);
}

bool Job::doKill()
{
180
    bool ret = archiveInterface()->doKill();
181
    if (!ret) {
182
        qCWarning(ARK) << "Killing does not seem to be supported here.";
183
    }
184 185 186 187
    return ret;
}

ListJob::ListJob(ReadOnlyArchiveInterface *interface, QObject *parent)
188 189 190 191
    : Job(interface, parent)
    , m_isSingleFolderArchive(true)
    , m_isPasswordProtected(false)
    , m_extractedFilesSize(0)
192
{
193
    qCDebug(ARK) << "ListJob started";
Laurent Montel's avatar
Laurent Montel committed
194
    connect(this, &ListJob::newEntry, this, &ListJob::onNewEntry);
195 196 197 198 199
}

void ListJob::doWork()
{
    emit description(this, i18n("Loading archive..."));
200
    connectToArchiveInterfaceSignals();
201
    bool ret = archiveInterface()->list();
202

203
    if (!archiveInterface()->waitForFinishedSignal()) {
204
        onFinished(ret);
205
    }
206 207
}

208
qlonglong ListJob::extractedFilesSize() const
209 210 211 212
{
    return m_extractedFilesSize;
}

213
bool ListJob::isPasswordProtected() const
214 215 216 217
{
    return m_isPasswordProtected;
}

218
bool ListJob::isSingleFolderArchive() const
219 220 221 222
{
    return m_isSingleFolderArchive;
}

223 224 225 226 227
void ListJob::onNewEntry(const ArchiveEntry& entry)
{
    m_extractedFilesSize += entry[ Size ].toLongLong();
    m_isPasswordProtected |= entry [ IsPasswordProtected ].toBool();

228 229
    if (m_isSingleFolderArchive) {
        const QString fileName(entry[FileName].toString());
230
        const QString basePath(fileName.split(QLatin1Char( '/' )).at(0));
231

232 233 234
        if (m_basePath.isEmpty()) {
            m_basePath = basePath;
            m_subfolderName = basePath;
235
        } else {
236 237 238 239
            if (m_basePath != basePath) {
                m_isSingleFolderArchive = false;
                m_subfolderName.clear();
            }
240 241 242 243
        }
    }
}

244
QString ListJob::subfolderName() const
245 246 247 248
{
    return m_subfolderName;
}

249
ExtractJob::ExtractJob(const QVariantList& files, const QString& destinationDir, const ExtractionOptions& options, ReadOnlyArchiveInterface *interface, QObject *parent)
250 251 252 253
    : Job(interface, parent)
    , m_files(files)
    , m_destinationDir(destinationDir)
    , m_options(options)
254
{
255
    qCDebug(ARK) << "ExtractJob created";
256
    setDefaultOptions();
257 258 259 260 261 262 263 264 265 266 267 268
}

void ExtractJob::doWork()
{
    QString desc;
    if (m_files.count() == 0) {
        desc = i18n("Extracting all files");
    } else {
        desc = i18np("Extracting one file", "Extracting %1 files", m_files.count());
    }
    emit description(this, desc);

269 270 271 272 273 274 275
    QFileInfo destDirInfo(m_destinationDir);
    if (destDirInfo.isDir() && (!destDirInfo.isWritable() || !destDirInfo.isExecutable())) {
        onError(xi18n("Could not write to destination <filename>%1</filename>.<nl/>Check whether you have sufficient permissions.", m_destinationDir), QString());
        onFinished(false);
        return;
    }

276
    connectToArchiveInterfaceSignals();
277

278
    qCDebug(ARK) << "Starting extraction with selected files:"
279 280 281
             << m_files
             << "Destination dir:" << m_destinationDir
             << "Options:" << m_options;
282

283
    bool ret = archiveInterface()->copyFiles(m_files, m_destinationDir, m_options);
284

285
    if (!archiveInterface()->waitForFinishedSignal()) {
286
        onFinished(ret);
287
    }
288 289
}

290
void ExtractJob::setDefaultOptions()
291
{
292 293
    ExtractionOptions defaultOptions;

294
    defaultOptions[QStringLiteral("PreservePaths")] = false;
295 296 297 298 299 300

    ExtractionOptions::const_iterator it = defaultOptions.constBegin();
    for (; it != defaultOptions.constEnd(); ++it) {
        if (!m_options.contains(it.key())) {
            m_options[it.key()] = it.value();
        }
301 302 303
    }
}

304 305 306 307 308
QString ExtractJob::destinationDirectory() const
{
    return m_destinationDir;
}

309 310 311 312 313
ExtractionOptions ExtractJob::extractionOptions() const
{
    return m_options;
}

314
AddJob::AddJob(const QStringList& files, const CompressionOptions& options , ReadWriteArchiveInterface *interface, QObject *parent)
315 316 317
    : Job(interface, parent)
    , m_files(files)
    , m_options(options)
318
{
319
    qCDebug(ARK) << "AddJob started";
320 321 322 323
}

void AddJob::doWork()
{
Elvis Angelaccio's avatar
Elvis Angelaccio committed
324
    qCDebug(ARK) << "AddJob: going to add" << m_files.count() << "file(s)";
325

326 327 328
    emit description(this, i18np("Adding a file", "Adding %1 files", m_files.count()));

    ReadWriteArchiveInterface *m_writeInterface =
329
        qobject_cast<ReadWriteArchiveInterface*>(archiveInterface());
330 331 332

    Q_ASSERT(m_writeInterface);

333
    connectToArchiveInterfaceSignals();
334 335
    bool ret = m_writeInterface->addFiles(m_files, m_options);

336
    if (!archiveInterface()->waitForFinishedSignal()) {
337
        onFinished(ret);
338
    }
339 340
}

341
DeleteJob::DeleteJob(const QVariantList& files, ReadWriteArchiveInterface *interface, QObject *parent)
342 343
    : Job(interface, parent)
    , m_files(files)
344 345 346 347 348 349 350 351
{
}

void DeleteJob::doWork()
{
    emit description(this, i18np("Deleting a file from the archive", "Deleting %1 files", m_files.count()));

    ReadWriteArchiveInterface *m_writeInterface =
352
        qobject_cast<ReadWriteArchiveInterface*>(archiveInterface());
353 354 355

    Q_ASSERT(m_writeInterface);

356
    connectToArchiveInterfaceSignals();
357
    bool ret = m_writeInterface->deleteFiles(m_files);
358

359
    if (!archiveInterface()->waitForFinishedSignal()) {
360
        onFinished(ret);
361
    }
362
}
363

364
} // namespace Kerfuffle
365

366