File indexing completed on 2025-01-05 05:09:28

0001 /*
0002     SPDX-FileCopyrightText: 2010-2018 Daniel Nicoletti <dantti12@gmail.com>
0003     SPDX-FileCopyrightText: 2023 Mike Noe <noeerover@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "NewPrinterNotification.h"
0009 #include "newprinternotificationadaptor.h"
0010 
0011 #include "pmkded_log.h"
0012 
0013 #include <KLocalizedString>
0014 #include <KNotification>
0015 
0016 #include <KCupsRequest.h>
0017 #include <ProcessRunner.h>
0018 
0019 #include <QDBusConnection>
0020 #include <QDBusMessage>
0021 #include <QDBusPendingCallWatcher>
0022 #include <QDBusPendingReply>
0023 #include <QDBusServiceWatcher>
0024 
0025 #define STATUS_SUCCESS 0
0026 #define STATUS_MODEL_MISMATCH 1
0027 #define STATUS_GENERIC_DRIVER 2
0028 #define STATUS_NO_DRIVER 3
0029 
0030 #define PRINTER_NAME "PrinterName"
0031 
0032 NewPrinterNotification::NewPrinterNotification(QObject *parent)
0033     : QObject(parent)
0034 {
0035     // Creates our new adaptor
0036     (void)new NewPrinterNotificationAdaptor(this);
0037 
0038     // Register the com.redhat.NewPrinterNotification interface
0039     if (!registerService()) {
0040         // in case registration fails due to another user or application running
0041         // keep an eye on it so we can register when available
0042         auto watcher = new QDBusServiceWatcher(QLatin1String("com.redhat.NewPrinterNotification"),
0043                                                QDBusConnection::systemBus(),
0044                                                QDBusServiceWatcher::WatchForUnregistration,
0045                                                this);
0046         connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, &NewPrinterNotification::registerService);
0047     }
0048 }
0049 
0050 NewPrinterNotification::~NewPrinterNotification()
0051 {
0052 }
0053 
0054 void NewPrinterNotification::GetReady()
0055 {
0056     qCDebug(PMKDED) << "GetReady";
0057     // This method is all about telling the user a new printer was detected
0058     auto notify = new KNotification(QLatin1String("GetReady"));
0059     notify->setComponentName(QLatin1String("printmanager"));
0060     notify->setIconName(QLatin1String("printer"));
0061     notify->setTitle(i18n("A New Printer was detected"));
0062     notify->setText(i18n("Configuring new printer..."));
0063     notify->sendEvent();
0064 }
0065 
0066 // status: 0
0067 // name: PSC_1400_series
0068 // mfg: HP
0069 // mdl: PSC 1400 series
0070 // des:
0071 // cmd: LDL,MLC,PML,DYN
0072 void NewPrinterNotification::NewPrinter(int status,
0073                                         const QString &name,
0074                                         const QString &make,
0075                                         const QString &model,
0076                                         const QString &description,
0077                                         const QString &cmd)
0078 {
0079     qCDebug(PMKDED) << status << name << make << model << description << cmd;
0080 
0081     // 1
0082     // "usb://Samsung/SCX-3400%20Series?serial=Z6Y1BQAC500079K&interface=1"
0083     // mfg "Samsung"
0084     // mdl "SCX-3400 Series" "" "SPL,FWV,PIC,BDN,EXT"
0085     // This method is all about telling the user a new printer was detected
0086     auto notify = new KNotification(QLatin1String("NewPrinterNotification"));
0087     notify->setComponentName(QLatin1String("printmanager"));
0088     notify->setIconName(QLatin1String("printer"));
0089     notify->setFlags(KNotification::Persistent);
0090 
0091     if (name.contains(QLatin1Char('/'))) {
0092         const QString devid = QString::fromLatin1("MFG:%1;MDL:%2;DES:%3;CMD:%4;").arg(make, model, description, cmd);
0093         setupPrinterNotification(notify, make, model, description, name + QLatin1Char('/') + devid);
0094     } else {
0095         notify->setProperty(PRINTER_NAME, name);
0096         // name is the name of the queue which hal_lpadmin has set up
0097         // automatically.
0098 
0099         if (status < STATUS_GENERIC_DRIVER) {
0100             notify->setTitle(i18n("The New Printer was Added"));
0101         } else {
0102             notify->setTitle(i18n("The New Printer is Missing Drivers"));
0103         }
0104 
0105         auto request = new KCupsRequest;
0106         connect(request, &KCupsRequest::finished, this, [this, notify, status, name](KCupsRequest *request) {
0107             const QString ppdFileName = request->printerPPD();
0108             // Get a list of missing executables
0109             getMissingExecutables(notify, status, name, ppdFileName);
0110             request->deleteLater();
0111         });
0112         request->getPrinterPPD(name);
0113     }
0114 }
0115 
0116 bool NewPrinterNotification::registerService()
0117 {
0118     if (!QDBusConnection::systemBus().registerService(QLatin1String("com.redhat.NewPrinterNotification"))) {
0119         qCWarning(PMKDED) << "unable to register service to dbus";
0120         return false;
0121     }
0122 
0123     if (!QDBusConnection::systemBus().registerObject(QLatin1String("/com/redhat/NewPrinterNotification"), this)) {
0124         qCWarning(PMKDED) << "unable to register object to dbus";
0125         return false;
0126     }
0127     return true;
0128 }
0129 
0130 void NewPrinterNotification::configurePrinter()
0131 {
0132     const QString printerName = sender()->property(PRINTER_NAME).toString();
0133     qCDebug(PMKDED) << "configure printer tool" << printerName;
0134     ProcessRunner::configurePrinter(printerName);
0135 }
0136 
0137 void NewPrinterNotification::printTestPage()
0138 {
0139     const QString printerName = sender()->property(PRINTER_NAME).toString();
0140     qCDebug(PMKDED) << "printing test page for" << printerName;
0141 
0142     auto request = new KCupsRequest;
0143     connect(request, &KCupsRequest::finished, request, &KCupsRequest::deleteLater);
0144     request->printTestPage(printerName, false);
0145 }
0146 
0147 void NewPrinterNotification::findDriver()
0148 {
0149     const QString printerName = sender()->property(PRINTER_NAME).toString();
0150     qCDebug(PMKDED) << "find driver for" << printerName;
0151 
0152     // This function will show the PPD browser dialog
0153     // to choose a better PPD to the already added printer
0154     ProcessRunner::changePrinterPPD(printerName);
0155 }
0156 
0157 void NewPrinterNotification::setupPrinterNotification(KNotification *notify,
0158                                                       const QString &make,
0159                                                       const QString &model,
0160                                                       const QString &description,
0161                                                       const QString &arg)
0162 {
0163     // name is a URI, no queue was generated, because no suitable
0164     // driver was found
0165     notify->setTitle(i18n("Missing printer driver"));
0166     if (!make.isEmpty() && !model.isEmpty()) {
0167         notify->setText(i18n("No printer driver for %1 %2.", make, model));
0168     } else if (!description.isEmpty()) {
0169         notify->setText(i18n("No printer driver for %1.", description));
0170     } else {
0171         notify->setText(i18n("No driver for this printer."));
0172     }
0173     auto searchAction = notify->addAction(i18n("Search"));
0174     connect(searchAction, &KNotificationAction::activated, this, [arg]() {
0175         qCDebug(PMKDED);
0176         // This function will show the PPD browser dialog
0177         // to choose a better PPD, queue name, location
0178         // in this case the printer was not added
0179         ProcessRunner::addPrinterFromDevice(arg);
0180     });
0181 
0182     notify->sendEvent();
0183 }
0184 
0185 void NewPrinterNotification::getMissingExecutables(KNotification *notify, int status, const QString &name, const QString &ppdFileName)
0186 {
0187     qCDebug(PMKDED) << "get missing executables" << ppdFileName;
0188     QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.fedoraproject.Config.Printing"),
0189                                                           QLatin1String("/org/fedoraproject/Config/Printing"),
0190                                                           QLatin1String("org.fedoraproject.Config.Printing"),
0191                                                           QLatin1String("MissingExecutables"));
0192     message << ppdFileName;
0193 
0194     QDBusPendingReply<QStringList> reply = QDBusConnection::sessionBus().asyncCall(message);
0195     auto watcher = new QDBusPendingCallWatcher(reply, this);
0196     connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, notify, status, name]() {
0197         watcher->deleteLater();
0198         QDBusPendingReply<QStringList> reply = *watcher;
0199         if (!reply.isValid()) {
0200             qCWarning(PMKDED) << "Invalid reply" << reply.error();
0201             notify->deleteLater();
0202             return;
0203         }
0204 
0205         const QStringList missingExecutables = reply;
0206         if (!missingExecutables.isEmpty()) {
0207             // TODO check with PackageKit about missing drivers
0208             qCWarning(PMKDED) << "Missing executables:" << missingExecutables;
0209             notify->deleteLater();
0210             return;
0211         } else if (status == STATUS_SUCCESS) {
0212             printerReadyNotification(notify, name);
0213         } else {
0214             // Model mismatch
0215             checkPrinterCurrentDriver(notify, name);
0216         }
0217     });
0218 }
0219 
0220 void NewPrinterNotification::checkPrinterCurrentDriver(KNotification *notify, const QString &name)
0221 {
0222     // Get the new printer attributes
0223     auto request = new KCupsRequest;
0224     connect(request, &KCupsRequest::finished, this, [this, notify, name](KCupsRequest *request) {
0225         request->deleteLater();
0226 
0227         QString driver;
0228         // Get the new printer driver
0229         if (!request->printers().isEmpty()) {
0230             const KCupsPrinter &printer = request->printers().first();
0231             driver = printer.makeAndModel();
0232         }
0233 
0234         // The cups request might have failed
0235         if (driver.isEmpty()) {
0236             notify->setText(i18n("'%1' has been added, please check its driver.", name));
0237             auto configAction = notify->addAction(i18n("Configure"));
0238             connect(configAction, &KNotificationAction::activated, this, &NewPrinterNotification::configurePrinter);
0239         } else {
0240             notify->setText(i18n("'%1' has been added, using the '%2' driver.", name, driver));
0241             auto testAction = notify->addAction(i18n("Print test page"));
0242             connect(testAction, &KNotificationAction::activated, this, &NewPrinterNotification::printTestPage);
0243             auto findAction = notify->addAction(i18n("Find driver"));
0244             connect(findAction, &KNotificationAction::activated, this, &NewPrinterNotification::findDriver);
0245         }
0246         notify->sendEvent();
0247     });
0248     request->getPrinterAttributes(name, false, {KCUPS_PRINTER_MAKE_AND_MODEL});
0249 }
0250 
0251 void NewPrinterNotification::printerReadyNotification(KNotification *notify, const QString &name)
0252 {
0253     notify->setText(i18n("'%1' is ready for printing.", name));
0254 
0255     auto testAction = notify->addAction(i18n("Print test page"));
0256     connect(testAction, &KNotificationAction::activated, this, &NewPrinterNotification::printTestPage);
0257 
0258     auto configAction = notify->addAction(i18n("Configure"));
0259     connect(configAction, &KNotificationAction::activated, this, &NewPrinterNotification::configurePrinter);
0260 
0261     notify->sendEvent();
0262 }
0263 
0264 #include "moc_NewPrinterNotification.cpp"