File indexing completed on 2024-04-14 05:38:11

0001 /***************************************************************************
0002  *   Copyright (C) 2008-2018 by Daniel Nicoletti                           *
0003  *   dantti12@gmail.com                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; see the file COPYING. If not, write to       *
0017  *   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,  *
0018  *   Boston, MA 02110-1301, USA.                                           *
0019  ***************************************************************************/
0020 
0021 #include <config.h>
0022 
0023 #include "PkTransaction.h"
0024 
0025 #include <KLocalizedString>
0026 #include <KMessageBox>
0027 #include <QPushButton>
0028 #include <KPixmapSequence>
0029 #include <KConfig>
0030 #include <KConfigGroup>
0031 
0032 #include <QLoggingCategory>
0033 
0034 #include <QtDBus/QDBusMessage>
0035 #include <QtDBus/QDBusConnection>
0036 #include <QTreeView>
0037 
0038 #include <Daemon>
0039 
0040 #include "Enum.h"
0041 #include "PkStrings.h"
0042 #include "RepoSig.h"
0043 #include "LicenseAgreement.h"
0044 #include "PkIcons.h"
0045 #include "ApplicationLauncher.h"
0046 #include "PackageModel.h"
0047 #include "Requirements.h"
0048 #include "PkTransactionProgressModel.h"
0049 #include "PkTransactionWidget.h"
0050 
0051 Q_DECLARE_LOGGING_CATEGORY(APPER_LIB)
0052 
0053 class PkTransactionPrivate
0054 {
0055 public:
0056     bool allowDeps;
0057     bool jobWatcher;
0058     bool handlingActionRequired;
0059     bool showingError; //This might replace the above
0060     qulonglong downloadSizeRemaining;
0061     PkTransaction::ExitStatus exitStatus;
0062     Transaction::Status status;
0063     Transaction::TransactionFlags flags;
0064     Transaction::Role originalRole;
0065     Transaction::Error error;
0066     Transaction::Role role;
0067     QStringList packages;
0068     ApplicationLauncher *launcher;
0069     QStringList files;
0070     QStringList newPackages;
0071     PackageModel *simulateModel;
0072     PkTransactionProgressModel *progressModel;
0073     QWidget *parentWindow;
0074     QDBusObjectPath tid;
0075     Transaction *transaction;
0076 };
0077 
0078 PkTransaction::PkTransaction(QObject *parent) :
0079     QObject(parent),
0080     d(new PkTransactionPrivate)
0081 {
0082     // for sanity we are finished till some transaction is set
0083     d->allowDeps = false;
0084     d->jobWatcher = false;
0085     d->handlingActionRequired = false;
0086     d->showingError = false;
0087     d->downloadSizeRemaining = 0;
0088     d->exitStatus = Success;
0089     d->status = Transaction::StatusUnknown;
0090     // for sanity we are trusted till an error is given and the user accepts
0091     d->flags = Transaction::TransactionFlagOnlyTrusted;
0092     d->originalRole = Transaction::RoleUnknown;
0093     d->role = Transaction::RoleUnknown;
0094     d->error = Transaction::ErrorUnknown;
0095     d->launcher = nullptr;
0096     d->simulateModel = nullptr;
0097     d->progressModel = new PkTransactionProgressModel(this);
0098     d->parentWindow = qobject_cast<QWidget*>(parent);
0099     d->transaction = nullptr;
0100 }
0101 
0102 PkTransaction::~PkTransaction()
0103 {
0104     // DO NOT disconnect the transaction here,
0105     // it might not exist when this happen
0106     delete d;
0107 }
0108 
0109 void PkTransaction::installFiles(const QStringList &files)
0110 {
0111 //    if (Daemon::global()->roles() & Transaction::RoleInstallFiles) {
0112         d->originalRole = Transaction::RoleInstallFiles;
0113         d->files = files;
0114         d->flags = Transaction::TransactionFlagOnlyTrusted | Transaction::TransactionFlagSimulate;
0115 
0116         setupTransaction(Daemon::installFiles(files, d->flags));
0117 //    } else {
0118 //        showError(i18n("Current backend does not support installing files."), i18n("Error"));
0119 //    }
0120 }
0121 
0122 void PkTransaction::installPackages(const QStringList &packages)
0123 {
0124 //    if (Daemon::global()->roles() & Transaction::RoleInstallPackages) {
0125         d->originalRole = Transaction::RoleInstallPackages;
0126         d->packages = packages;
0127         d->flags = Transaction::TransactionFlagOnlyTrusted | Transaction::TransactionFlagSimulate;
0128 
0129         setupTransaction(Daemon::installPackages(d->packages, d->flags));
0130 //    } else {
0131 //        showError(i18n("Current backend does not support installing packages."), i18n("Error"));
0132 //    }
0133 }
0134 
0135 void PkTransaction::removePackages(const QStringList &packages)
0136 {
0137 //    if (Daemon::global()->roles() & Transaction::RoleRemovePackages) {
0138         d->originalRole = Transaction::RoleRemovePackages;
0139         d->allowDeps = true; // *was* false, Default to avoid dependencies removal unless simulate says so, except for https://bugs.kde.org/show_bug.cgi?id=315063
0140         d->packages = packages;
0141         d->flags = Transaction::TransactionFlagOnlyTrusted | Transaction::TransactionFlagSimulate;
0142 
0143         setupTransaction(Daemon::removePackages(d->packages, d->allowDeps, AUTOREMOVE, d->flags));
0144 //    } else {
0145 //        showError(i18n("The current backend does not support removing packages."), i18n("Error"));
0146 //    }
0147 }
0148 
0149 void PkTransaction::updatePackages(const QStringList &packages, bool downloadOnly)
0150 {
0151 //    if (Daemon::global()->roles() & Transaction::RoleUpdatePackages) {
0152         d->originalRole = Transaction::RoleUpdatePackages;
0153         d->packages = packages;
0154         if (downloadOnly) {
0155             // Don't simulate if we are just downloading
0156             d->flags = Transaction::TransactionFlagOnlyDownload;
0157         } else {
0158             d->flags = Transaction::TransactionFlagOnlyTrusted | Transaction::TransactionFlagSimulate;
0159         }
0160 
0161         setupTransaction(Daemon::updatePackages(d->packages, d->flags));
0162 //    } else {
0163 //        showError(i18n("The current backend does not support updating packages."), i18n("Error"));
0164 //    }
0165 }
0166 
0167 void PkTransaction::refreshCache(bool force)
0168 {
0169     setupTransaction(Daemon::refreshCache(force));
0170 }
0171 
0172 void PkTransaction::installPackages()
0173 {
0174     setupTransaction(Daemon::installPackages(d->packages, d->flags));
0175 }
0176 
0177 void PkTransaction::installFiles()
0178 {
0179     setupTransaction(Daemon::installFiles(d->files, d->flags));
0180 }
0181 
0182 void PkTransaction::removePackages()
0183 {
0184     setupTransaction(Daemon::removePackages(d->packages, d->allowDeps, AUTOREMOVE, d->flags));
0185 }
0186 
0187 void PkTransaction::updatePackages()
0188 {
0189     setupTransaction(Daemon::updatePackages(d->packages, d->flags));
0190 }
0191 
0192 void PkTransaction::requeueTransaction()
0193 {
0194     auto requires = qobject_cast<Requirements *>(sender());
0195     if (requires) {
0196         // As we have requires allow deps removal
0197         d->allowDeps = true;
0198         if (!requires->trusted()) {
0199             // Set only trusted to false, to do as the user asked
0200             setTrusted(false);
0201         }
0202     }
0203 
0204     // Delete the simulate model
0205     if (d->simulateModel) {
0206         d->simulateModel->deleteLater();
0207         d->simulateModel = nullptr;
0208     }
0209 
0210     // We are not handling any required action yet for the requeued transaction.
0211     // Without this a second license agreement f.e. does not get shown,
0212     // see http://bugs.kde.org/show_bug.cgi?id=326619
0213     d->handlingActionRequired = false;
0214 
0215     switch (d->originalRole) {
0216     case Transaction::RoleRemovePackages:
0217         removePackages();
0218         break;
0219     case Transaction::RoleInstallPackages:
0220         installPackages();
0221         break;
0222     case Transaction::RoleInstallFiles:
0223         installFiles();
0224         break;
0225     case Transaction::RoleUpdatePackages:
0226         updatePackages();
0227         break;
0228     default :
0229         setExitStatus(Failed);
0230         return;
0231     }
0232 }
0233 
0234 void PkTransaction::slotErrorCode(Transaction::Error error, const QString &details)
0235 {
0236     qCDebug(APPER_LIB) << "errorCode: " << error << details;
0237     d->error = error;
0238 
0239     if (d->handlingActionRequired) {
0240         // We are already handling required actions
0241         // like eulaRequired() and repoSignatureRequired()
0242         return;
0243     }
0244 
0245     switch (error) {
0246     case Transaction::ErrorTransactionCancelled:
0247     case Transaction::ErrorProcessKill:
0248         // these errors should be ignored
0249         break;
0250     case Transaction::ErrorGpgFailure:
0251     case Transaction::ErrorBadGpgSignature:
0252     case Transaction::ErrorMissingGpgSignature:
0253     case Transaction::ErrorCannotInstallRepoUnsigned:
0254     case Transaction::ErrorCannotUpdateRepoUnsigned:
0255     {
0256         if (d->role == Transaction::RoleRefreshCache) {
0257             // We are not installing anything
0258             KMessageBox::information(d->parentWindow, details, PkStrings::error(error));
0259             return;
0260         }
0261 
0262         d->handlingActionRequired = true;
0263         int ret = KMessageBox::warningYesNo(d->parentWindow,
0264                                             i18n("You are about to install unsigned packages that can compromise your system, "
0265                                             "as it is impossible to verify if the software came from a trusted "
0266                                             "source.\n\nAre you sure you want to proceed with the installation?"),
0267                                             i18n("Installing unsigned software"));
0268         if (ret == KMessageBox::Yes) {
0269             // Set only trusted to false, to do as the user asked
0270             setTrusted(false);
0271             requeueTransaction();
0272         } else {
0273             setExitStatus(Cancelled);
0274         }
0275         d->handlingActionRequired = false;
0276         return;
0277     }
0278     default:
0279         d->showingError = true;
0280         showSorry(PkStrings::error(error), PkStrings::errorMessage(error), QString(details).replace(QLatin1Char('\n'), QLatin1String("<br>")));
0281 
0282         // when we receive an error we are done
0283         setExitStatus(Failed);
0284     }
0285 }
0286 
0287 void PkTransaction::slotEulaRequired(const QString &eulaID, const QString &packageID, const QString &vendor, const QString &licenseAgreement)
0288 {
0289     if (d->handlingActionRequired) {
0290         // if its true means that we alread passed here
0291         d->handlingActionRequired = false;
0292         return;
0293     } else {
0294         d->handlingActionRequired = true;
0295     }
0296 
0297     auto eula = new LicenseAgreement(eulaID, packageID, vendor, licenseAgreement, d->parentWindow);
0298     connect(eula, &LicenseAgreement::accepted, this, [this, eula] () {
0299         qCDebug(APPER_LIB) << "Accepting EULA" << eula->id();
0300         setupTransaction(Daemon::acceptEula(eula->id()));
0301     });
0302     connect(eula, &LicenseAgreement::rejected, this, &PkTransaction::reject);
0303     showDialog(eula);
0304 }
0305 
0306 void PkTransaction::slotChanged()
0307 {
0308     auto transaction = qobject_cast<Transaction*>(sender());
0309     d->downloadSizeRemaining = transaction->downloadSizeRemaining();
0310     d->role = transaction->role();
0311 
0312     if (!d->jobWatcher) {
0313         return;
0314     }
0315 
0316     QDBusObjectPath _tid = transaction->tid();
0317     if (d->tid != _tid && !(d->flags & Transaction::TransactionFlagSimulate)) {
0318         d->tid = _tid;
0319         // if the transaction changed and
0320         // the user wants the watcher send the tid
0321         QDBusMessage message;
0322         message = QDBusMessage::createMethodCall(QLatin1String("org.kde.apperd"),
0323                                                  QLatin1String("/"),
0324                                                  QLatin1String("org.kde.apperd"),
0325                                                  QLatin1String("WatchTransaction"));
0326         // Use our own cached tid to avoid crashes
0327         message << qVariantFromValue(_tid);
0328         if (!QDBusConnection::sessionBus().send(message)) {
0329             qCWarning(APPER_LIB) << "Failed to put WatchTransaction on the DBus queue";
0330         }
0331     }
0332 }
0333 
0334 void PkTransaction::slotMediaChangeRequired(Transaction::MediaType type, const QString &id, const QString &text)
0335 {
0336     Q_UNUSED(id)
0337 
0338     d->handlingActionRequired = true;
0339     int ret = KMessageBox::questionYesNo(d->parentWindow,
0340                                          PkStrings::mediaMessage(type, text),
0341                                          i18n("A media change is required"),
0342                                          KStandardGuiItem::cont(),
0343                                          KStandardGuiItem::cancel());
0344     d->handlingActionRequired = false;
0345 
0346     // if the user clicked continue we got yes
0347     if (ret == KMessageBox::Yes) {
0348         requeueTransaction();
0349     } else {
0350         setExitStatus(Cancelled);
0351     }
0352 }
0353 
0354 void PkTransaction::slotRepoSignature(const QString &packageID,
0355                                       const QString &repoName,
0356                                       const QString &keyUrl,
0357                                       const QString &keyUserid,
0358                                       const QString &keyId,
0359                                       const QString &keyFingerprint,
0360                                       const QString &keyTimestamp,
0361                                       Transaction::SigType type)
0362 {
0363     if (d->handlingActionRequired) {
0364         // if its true means that we alread passed here
0365         d->handlingActionRequired = false;
0366         return;
0367     } else {
0368         d->handlingActionRequired = true;
0369     }
0370 
0371     auto repoSig = new RepoSig(packageID, repoName, keyUrl, keyUserid, keyId, keyFingerprint, keyTimestamp, type, d->parentWindow);
0372     connect(repoSig, &RepoSig::accepted, this, [this, repoSig] () {
0373         qCDebug(APPER_LIB) << "Installing Signature" << repoSig->keyID();
0374         setupTransaction(Daemon::installSignature(repoSig->sigType(), repoSig->keyID(), repoSig->packageID()));
0375     });
0376     connect(repoSig, &RepoSig::rejected, this, &PkTransaction::reject);
0377     showDialog(repoSig);
0378 }
0379 
0380 void PkTransaction::slotFinished(Transaction::Exit status)
0381 {
0382     // Clear the model to don't keep trash when reusing the transaction
0383     d->progressModel->clear();
0384 
0385     Requirements *requires = nullptr;
0386     Transaction::Role _role = qobject_cast<Transaction*>(sender())->role();
0387     d->transaction = nullptr; // Will be deleted later
0388     qCDebug(APPER_LIB) << status << _role;
0389 
0390     switch (_role) {
0391     case Transaction::RoleInstallSignature:
0392     case Transaction::RoleAcceptEula:
0393         if (status == Transaction::ExitSuccess) {
0394             // if the required action was performed with success
0395             // requeue our main transaction
0396             requeueTransaction();
0397             return;
0398         }
0399         break;
0400     default:
0401         break;
0402     }
0403 
0404     switch(status) {
0405     case Transaction::ExitSuccess:
0406         // Check if we are just simulating
0407         if (d->flags & Transaction::TransactionFlagSimulate) {
0408             // Disable the simulate flag
0409             d->flags ^= Transaction::TransactionFlagSimulate;
0410             d->simulateModel->finished();
0411 
0412             // Remove the transaction packages
0413             for (const QString &packageID : qAsConst(d->packages)) {
0414                 d->simulateModel->removePackage(packageID);
0415             }
0416 
0417             d->newPackages = d->simulateModel->packagesWithInfo(Transaction::InfoInstalling);
0418             if (_role == Transaction::RoleInstallPackages) {
0419                 d->newPackages << d->packages;
0420                 d->newPackages.removeDuplicates();
0421             }
0422 
0423             requires = new Requirements(d->simulateModel, d->parentWindow);
0424             requires->setDownloadSizeRemaining(d->downloadSizeRemaining);
0425             connect(requires, &Requirements::accepted, this, &PkTransaction::requeueTransaction);
0426             connect(requires, &Requirements::rejected, this, &PkTransaction::reject);
0427             if (requires->shouldShow()) {
0428                 showDialog(requires);
0429             } else {
0430                 requires->deleteLater();
0431 
0432                 // Since we removed the Simulate Flag this will procced
0433                 // with the actual action
0434                 requeueTransaction();
0435             }
0436         } else {
0437             KConfig config(QLatin1String("apper"));
0438             KConfigGroup transactionGroup(&config, "Transaction");
0439             bool showApp = transactionGroup.readEntry("ShowApplicationLauncher", true);
0440             if (showApp &&
0441                     !d->newPackages.isEmpty() &&
0442                     (_role == Transaction::RoleInstallPackages ||
0443                      _role == Transaction::RoleInstallFiles ||
0444                      _role == Transaction::RoleRemovePackages ||
0445                      _role == Transaction::RoleUpdatePackages)) {
0446                 // When installing files or updates that involves new packages
0447                 // try to resolve the available packages at simulation time
0448                 // to maybe show the user the new applications that where installed
0449                 if (d->launcher) {
0450                     delete d->launcher;
0451                 }
0452                 d->launcher = new ApplicationLauncher(d->parentWindow);
0453                 connect(d->transaction, &Transaction::files, d->launcher, &ApplicationLauncher::files);
0454 
0455                 setupTransaction(Daemon::getFiles(d->newPackages));
0456                 d->newPackages.clear();
0457                 return; // avoid the exit code
0458             } else if (_role == Transaction::RoleGetFiles &&
0459                        d->launcher &&
0460                        d->launcher->hasApplications()) {
0461                 // if we have a launcher and the laucher has applications
0462                 // show them to the user
0463                 showDialog(d->launcher);
0464                 connect(d->launcher, &ApplicationLauncher::finished, this, &PkTransaction::setExitStatus);
0465                 return;
0466             }
0467             setExitStatus(Success);
0468         }
0469         break;
0470     case Transaction::ExitNeedUntrusted:
0471     case Transaction::ExitKeyRequired:
0472     case Transaction::ExitEulaRequired:
0473     case Transaction::ExitMediaChangeRequired:
0474         qCDebug(APPER_LIB) << "finished KeyRequired or EulaRequired: " << status;
0475         if (!d->handlingActionRequired) {
0476             qCDebug(APPER_LIB) << "Not Handling Required Action";
0477             setExitStatus(Failed);
0478         }
0479         break;
0480     case Transaction::ExitCancelled:
0481         // Avoid crash in case we are showing an error
0482         if (!d->showingError) {
0483             setExitStatus(Cancelled);
0484         }
0485         break;
0486     case Transaction::ExitFailed:
0487         if (!d->handlingActionRequired && !d->showingError) {
0488             qCDebug(APPER_LIB) << "Yep, we failed.";
0489             setExitStatus(Failed);
0490         }
0491         break;
0492     default :
0493         qCDebug(APPER_LIB) << "finished default" << status;
0494         setExitStatus(Failed);
0495         break;
0496     }
0497 }
0498 
0499 PkTransaction::ExitStatus PkTransaction::exitStatus() const
0500 {
0501     return d->exitStatus;
0502 }
0503 
0504 bool PkTransaction::isFinished() const
0505 {
0506     qCDebug(APPER_LIB) << d->transaction->status() << d->transaction->role();
0507     return d->transaction->status() == Transaction::StatusFinished;
0508 }
0509 
0510 PackageModel *PkTransaction::simulateModel() const
0511 {
0512     return d->simulateModel;
0513 }
0514 
0515 uint PkTransaction::percentage() const
0516 {
0517     if (d->transaction) {
0518         return d->transaction->percentage();
0519     }
0520     return 0;
0521 }
0522 
0523 uint PkTransaction::remainingTime() const
0524 {
0525     if (d->transaction) {
0526         return d->transaction->remainingTime();
0527     }
0528     return 0;
0529 }
0530 
0531 uint PkTransaction::speed() const
0532 {
0533     if (d->transaction) {
0534         return d->transaction->speed();
0535     }
0536     return 0;
0537 }
0538 
0539 qulonglong PkTransaction::downloadSizeRemaining() const
0540 {
0541     if (d->transaction) {
0542         return d->transaction->downloadSizeRemaining();
0543     }
0544     return 0;
0545 }
0546 
0547 Transaction::Status PkTransaction::status() const
0548 {
0549     if (d->transaction) {
0550         return d->transaction->status();
0551     }
0552     return Transaction::StatusUnknown;
0553 }
0554 
0555 Transaction::Role PkTransaction::role() const
0556 {
0557     if (d->transaction) {
0558         return d->transaction->role();
0559     }
0560     return Transaction::RoleUnknown;
0561 }
0562 
0563 bool PkTransaction::allowCancel() const
0564 {
0565     if (d->transaction) {
0566         return d->transaction->allowCancel();
0567     }
0568     return false;
0569 }
0570 
0571 Transaction::TransactionFlags PkTransaction::transactionFlags() const
0572 {
0573     if (d->transaction) {
0574         return d->transaction->transactionFlags();
0575     }
0576     return Transaction::TransactionFlagNone;
0577 }
0578 
0579 void PkTransaction::getUpdateDetail(const QString &packageID)
0580 {
0581     setupTransaction(Daemon::getUpdateDetail(packageID));
0582 }
0583 
0584 void PkTransaction::getUpdates()
0585 {
0586     setupTransaction(Daemon::getUpdates());
0587 }
0588 
0589 void PkTransaction::cancel()
0590 {
0591     if (d->transaction) {
0592         d->transaction->cancel();
0593     }
0594 }
0595 
0596 void PkTransaction::setTrusted(bool trusted)
0597 {
0598     if (trusted) {
0599         d->flags |= Transaction::TransactionFlagOnlyTrusted;
0600     } else {
0601         d->flags ^= Transaction::TransactionFlagOnlyTrusted;
0602     }
0603 }
0604 
0605 void PkTransaction::setExitStatus(int status)
0606 {
0607     qCDebug(APPER_LIB) << status;
0608     if (d->launcher) {
0609         d->launcher->deleteLater();
0610         d->launcher = nullptr;
0611     }
0612 
0613     d->exitStatus = static_cast<PkTransaction::ExitStatus>(status);
0614     if (!d->handlingActionRequired || !d->showingError) {
0615         emit finished(d->exitStatus);
0616     }
0617 }
0618 
0619 void PkTransaction::reject()
0620 {
0621     setExitStatus(Cancelled);
0622 }
0623 
0624 void PkTransaction::setupTransaction(Transaction *transaction)
0625 {
0626     // Clear the model to don't keep trash when reusing the transaction
0627     d->progressModel->clear();
0628 
0629     d->transaction = transaction;
0630     if (!(transaction->transactionFlags() & Transaction::TransactionFlagSimulate) &&
0631             transaction->role() != Transaction::RoleGetUpdates &&
0632             transaction->role() != Transaction::RoleGetUpdateDetail) {
0633         connect(transaction, &Transaction::repoDetail, d->progressModel, &PkTransactionProgressModel::currentRepo);
0634         connect(transaction, &Transaction::package, d->progressModel, &PkTransactionProgressModel::currentPackage);
0635         connect(transaction, &Transaction::itemProgress, d->progressModel, &PkTransactionProgressModel::itemProgress);
0636     }
0637 
0638     connect(transaction, &Transaction::updateDetail, this, &PkTransaction::updateDetail);
0639     connect(transaction, &Transaction::package, this, &PkTransaction::package);
0640     connect(transaction, &Transaction::errorCode, this, &PkTransaction::errorCode);
0641 
0642     // Required actions
0643     connect(transaction, &Transaction::allowCancelChanged, this, &PkTransaction::allowCancelChanged);
0644     connect(transaction, &Transaction::downloadSizeRemainingChanged, this, &PkTransaction::downloadSizeRemainingChanged);
0645     connect(transaction, &Transaction::elapsedTimeChanged, this, &PkTransaction::elapsedTimeChanged);
0646     connect(transaction, &Transaction::isCallerActiveChanged, this, &PkTransaction::isCallerActiveChanged);
0647     connect(transaction, &Transaction::lastPackageChanged, this, &PkTransaction::lastPackageChanged);
0648     connect(transaction, &Transaction::percentageChanged, this, &PkTransaction::percentageChanged);
0649     connect(transaction, &Transaction::remainingTimeChanged, this, &PkTransaction::remainingTimeChanged);
0650     connect(transaction, &Transaction::roleChanged, this, &PkTransaction::roleChanged);
0651     connect(transaction, &Transaction::speedChanged, this, &PkTransaction::speedChanged);
0652     connect(transaction, &Transaction::statusChanged, this, &PkTransaction::statusChanged);
0653     connect(transaction, &Transaction::transactionFlagsChanged, this, &PkTransaction::transactionFlagsChanged);
0654     connect(transaction, &Transaction::uidChanged, this, &PkTransaction::uidChanged);
0655 
0656     connect(transaction, &Transaction::downloadSizeRemainingChanged, this, &PkTransaction::slotChanged);
0657     connect(transaction, &Transaction::errorCode, this, &PkTransaction::slotErrorCode);
0658     connect(transaction, &Transaction::eulaRequired, this, &PkTransaction::slotEulaRequired);
0659     connect(transaction, &Transaction::mediaChangeRequired, this, &PkTransaction::slotMediaChangeRequired);
0660     connect(transaction, &Transaction::repoSignatureRequired, this, &PkTransaction::slotRepoSignature);
0661 
0662     connect(transaction, &Transaction::finished, this, &PkTransaction::slotFinished);
0663 
0664     if (d->flags & Transaction::TransactionFlagSimulate) {
0665         d->simulateModel = new PackageModel(this);
0666         connect(d->transaction, &Transaction::package, d->simulateModel, &PackageModel::addNotSelectedPackage);
0667     }
0668 
0669 #ifdef HAVE_DEBCONFKDE
0670     QString _tid = transaction->tid().path();
0671     QString socket;
0672     // Build a socket path like /tmp/1761_edeceabd_data_debconf
0673     socket = QLatin1String("/tmp") % _tid % QLatin1String("_debconf");
0674     QDBusMessage message;
0675     message = QDBusMessage::createMethodCall(QLatin1String("org.kde.apperd"),
0676                                              QLatin1String("/"),
0677                                              QLatin1String("org.kde.apperd"),
0678                                              QLatin1String("SetupDebconfDialog"));
0679     // Use our own cached tid to avoid crashes
0680     message << qVariantFromValue(_tid);
0681     message << qVariantFromValue(socket);
0682     if (d->parentWindow) {
0683         message << qVariantFromValue(static_cast<uint>(d->parentWindow->effectiveWinId()));
0684     } else {
0685         message << qVariantFromValue(0u);
0686     }
0687 
0688     if (!QDBusConnection::sessionBus().send(message)) {
0689         qCWarning(APPER_LIB) << "Failed to put SetupDebconfDialog message in DBus queue";
0690     }
0691 
0692     transaction->setHints(QLatin1String("frontend-socket=") % socket);
0693 #endif //HAVE_DEBCONFKDE
0694 }
0695 
0696 void PkTransaction::showDialog(QDialog *dlg)
0697 {
0698     auto widget = qobject_cast<PkTransactionWidget *>(d->parentWindow);
0699     if (!widget || widget->isCancelVisible()) {
0700         dlg->setModal(d->parentWindow);
0701         dlg->show();
0702     } else {
0703         dlg->setProperty("embedded", true);
0704         emit dialog(dlg);
0705     }
0706 }
0707 
0708 void PkTransaction::showError(const QString &title, const QString &description, const QString &details)
0709 {
0710     auto widget = qobject_cast<PkTransactionWidget *>(d->parentWindow);
0711     if (!widget || widget->isCancelVisible()) {
0712         if (details.isEmpty()) {
0713             if (d->parentWindow) {
0714                 KMessageBox::error(d->parentWindow, description, title);
0715             } else {
0716                 KMessageBox::errorWId(0, description, title);
0717             }
0718         } else {
0719             KMessageBox::detailedError(d->parentWindow, description, details, title);
0720         }
0721     } else {
0722         emit errorMessage(title, description, details);
0723     }
0724 }
0725 
0726 void PkTransaction::showSorry(const QString &title, const QString &description, const QString &details)
0727 {
0728     auto widget = qobject_cast<PkTransactionWidget *>(d->parentWindow);
0729     if (!widget || widget->isCancelVisible()) {
0730         if (details.isEmpty()) {
0731             KMessageBox::error(d->parentWindow, description, title);
0732         } else {
0733             KMessageBox::detailedError(d->parentWindow, description, details, title);
0734         }
0735     } else {
0736         emit sorry(title, description, details);
0737     }
0738 }
0739 
0740 QString PkTransaction::title() const
0741 {
0742     return PkStrings::action(d->originalRole, d->flags);
0743 }
0744 
0745 Transaction::Role PkTransaction::cachedRole() const
0746 {
0747     return d->role;
0748 }
0749 
0750 Transaction::TransactionFlags PkTransaction::flags() const
0751 {
0752     return d->flags;
0753 }
0754 
0755 PkTransactionProgressModel *PkTransaction::progressModel() const
0756 {
0757     return d->progressModel;
0758 }
0759 
0760 void PkTransaction::enableJobWatcher(bool enable)
0761 {
0762     d->jobWatcher = enable;
0763 }
0764 
0765 #include "moc_PkTransaction.cpp"