File indexing completed on 2024-06-16 04:55:59

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     crypto/taskcollection.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
0006 
0007     SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
0008     SPDX-FileContributor: Intevation GmbH
0009 
0010     SPDX-License-Identifier: GPL-2.0-or-later
0011 */
0012 
0013 #include <config-kleopatra.h>
0014 
0015 #include "taskcollection.h"
0016 
0017 #include "kleopatra_debug.h"
0018 #include "task.h"
0019 
0020 #include <Libkleo/GnuPG>
0021 
0022 #include <algorithm>
0023 #include <map>
0024 
0025 #include <cmath>
0026 
0027 #include <KLocalizedString>
0028 
0029 using namespace Kleo;
0030 using namespace Kleo::Crypto;
0031 
0032 class TaskCollection::Private
0033 {
0034     TaskCollection *const q;
0035 
0036 public:
0037     explicit Private(TaskCollection *qq);
0038 
0039     void taskProgress();
0040     void taskResult(const std::shared_ptr<const Task::Result> &);
0041     void taskStarted();
0042     void calculateAndEmitProgress();
0043 
0044     std::map<int, std::shared_ptr<Task>> m_tasks;
0045     mutable quint64 m_totalProgress;
0046     mutable quint64 m_progress;
0047     unsigned int m_nCompleted;
0048     unsigned int m_nErrors;
0049     bool m_errorOccurred;
0050     bool m_doneEmitted;
0051 };
0052 
0053 TaskCollection::Private::Private(TaskCollection *qq)
0054     : q(qq)
0055     , m_totalProgress(0)
0056     , m_progress(0)
0057     , m_nCompleted(0)
0058     , m_nErrors(0)
0059     , m_errorOccurred(false)
0060     , m_doneEmitted(false)
0061 {
0062 }
0063 
0064 int TaskCollection::numberOfCompletedTasks() const
0065 {
0066     return d->m_nCompleted;
0067 }
0068 
0069 size_t TaskCollection::size() const
0070 {
0071     return d->m_tasks.size();
0072 }
0073 
0074 bool TaskCollection::allTasksCompleted() const
0075 {
0076     Q_ASSERT(d->m_nCompleted <= d->m_tasks.size());
0077     return d->m_nCompleted == d->m_tasks.size();
0078 }
0079 
0080 void TaskCollection::Private::taskProgress()
0081 {
0082     calculateAndEmitProgress();
0083 }
0084 
0085 void TaskCollection::Private::taskResult(const std::shared_ptr<const Task::Result> &result)
0086 {
0087     Q_ASSERT(result);
0088     ++m_nCompleted;
0089 
0090     if (result->hasError()) {
0091         m_errorOccurred = true;
0092         ++m_nErrors;
0093     }
0094     calculateAndEmitProgress();
0095     Q_EMIT q->result(result);
0096     if (!m_doneEmitted && q->allTasksCompleted()) {
0097         Q_EMIT q->done();
0098         m_doneEmitted = true;
0099     }
0100 }
0101 
0102 void TaskCollection::Private::taskStarted()
0103 {
0104     const Task *const task = qobject_cast<Task *>(q->sender());
0105     Q_ASSERT(task);
0106     Q_ASSERT(m_tasks.find(task->id()) != m_tasks.end());
0107     Q_EMIT q->started(m_tasks[task->id()]);
0108     calculateAndEmitProgress(); // start Knight-Rider-Mode right away (gpgsm doesn't report _any_ progress).
0109     if (m_doneEmitted) {
0110         // We are not done anymore, one task restarted.
0111         m_nCompleted--;
0112         m_nErrors--;
0113         m_doneEmitted = false;
0114     }
0115 }
0116 
0117 void TaskCollection::Private::calculateAndEmitProgress()
0118 {
0119     quint64 total = 0;
0120     quint64 processed = 0;
0121 
0122     static bool haveWorkingProgress = engineIsVersion(2, 1, 15);
0123     if (!haveWorkingProgress) {
0124         // GnuPG before 2.1.15 would overflow on progress values > max int.
0125         // and did not emit a total for our Qt data types.
0126         // As we can't know if it overflowed or what the total is we just knight
0127         // rider in that case
0128         if (m_doneEmitted) {
0129             Q_EMIT q->progress(1, 1);
0130         } else {
0131             Q_EMIT q->progress(0, 0);
0132         }
0133         return;
0134     }
0135 
0136     bool unknowable = false;
0137     for (auto it = m_tasks.begin(), end = m_tasks.end(); it != end; ++it) {
0138         // Sum up progress and totals
0139         const std::shared_ptr<Task> &i = it->second;
0140         Q_ASSERT(i);
0141         if (!i->totalProgress()) {
0142             // There still might be jobs for which we don't know the progress.
0143             qCDebug(KLEOPATRA_LOG) << "Task: " << i->label() << " has no total progress set. ";
0144             unknowable = true;
0145             break;
0146         }
0147         processed += i->currentProgress();
0148         total += i->totalProgress();
0149     }
0150 
0151     m_totalProgress = total;
0152     m_progress = processed;
0153     if (!unknowable && processed && total >= processed) {
0154         // Scale down to avoid range issues.
0155         int scaled = 1000 * (m_progress / static_cast<double>(m_totalProgress));
0156         qCDebug(KLEOPATRA_LOG) << "Collection Progress: " << scaled << " total: " << 1000;
0157         if (total == processed) {
0158             // This can happen when an output is finalizing, e.g. extracting an
0159             // archive.
0160             Q_EMIT q->progress(0, 0);
0161         } else {
0162             Q_EMIT q->progress(scaled, 1000);
0163         }
0164     } else {
0165         if (total < processed) {
0166             qCDebug(KLEOPATRA_LOG) << "Total progress is smaller then current progress.";
0167         }
0168         // Knight rider.
0169         Q_EMIT q->progress(0, 0);
0170     }
0171 }
0172 
0173 TaskCollection::TaskCollection(QObject *parent)
0174     : QObject(parent)
0175     , d(new Private(this))
0176 {
0177 }
0178 
0179 TaskCollection::~TaskCollection()
0180 {
0181 }
0182 
0183 bool TaskCollection::isEmpty() const
0184 {
0185     return d->m_tasks.empty();
0186 }
0187 
0188 bool TaskCollection::errorOccurred() const
0189 {
0190     return d->m_errorOccurred;
0191 }
0192 
0193 bool TaskCollection::allTasksHaveErrors() const
0194 {
0195     return d->m_nErrors == d->m_nCompleted;
0196 }
0197 
0198 std::shared_ptr<Task> TaskCollection::taskById(int id) const
0199 {
0200     const auto it = d->m_tasks.find(id);
0201     return it != d->m_tasks.end() ? it->second : std::shared_ptr<Task>();
0202 }
0203 
0204 std::vector<std::shared_ptr<Task>> TaskCollection::tasks() const
0205 {
0206     std::vector<std::shared_ptr<Task>> res;
0207     res.reserve(d->m_tasks.size());
0208     for (auto it = d->m_tasks.begin(), end = d->m_tasks.end(); it != end; ++it) {
0209         res.push_back(it->second);
0210     }
0211     return res;
0212 }
0213 
0214 void TaskCollection::setTasks(const std::vector<std::shared_ptr<Task>> &tasks)
0215 {
0216     for (const std::shared_ptr<Task> &i : tasks) {
0217         Q_ASSERT(i);
0218         d->m_tasks[i->id()] = i;
0219         connect(i.get(), &Task::progress, this, [this]() {
0220             d->taskProgress();
0221         });
0222         connect(i.get(),
0223                 SIGNAL(result(std::shared_ptr<const Kleo::Crypto::Task::Result>)),
0224                 this,
0225                 SLOT(taskResult(std::shared_ptr<const Kleo::Crypto::Task::Result>)));
0226         connect(i.get(), SIGNAL(started()), this, SLOT(taskStarted()));
0227     }
0228 }
0229 
0230 #include "moc_taskcollection.cpp"