diff --git a/libkcups/JobModel.cpp b/libkcups/JobModel.cpp index 4c77673..73c5174 100644 --- a/libkcups/JobModel.cpp +++ b/libkcups/JobModel.cpp @@ -1,637 +1,641 @@ /*************************************************************************** * Copyright (C) 2010-2018 by Daniel Nicoletti * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "JobModel.h" #include #include #include #include #include #include #include #include #include #include #include JobModel::JobModel(QObject *parent) : QStandardItemModel(parent) { setHorizontalHeaderItem(ColStatus, new QStandardItem(i18n("Status"))); setHorizontalHeaderItem(ColName, new QStandardItem(i18n("Name"))); setHorizontalHeaderItem(ColUser, new QStandardItem(i18n("User"))); setHorizontalHeaderItem(ColCreated, new QStandardItem(i18n("Created"))); setHorizontalHeaderItem(ColCompleted, new QStandardItem(i18n("Completed"))); setHorizontalHeaderItem(ColPages, new QStandardItem(i18n("Pages"))); setHorizontalHeaderItem(ColProcessed, new QStandardItem(i18n("Processed"))); setHorizontalHeaderItem(ColSize, new QStandardItem(i18n("Size"))); setHorizontalHeaderItem(ColStatusMessage, new QStandardItem(i18n("Status Message"))); setHorizontalHeaderItem(ColPrinter, new QStandardItem(i18n("Printer"))); setHorizontalHeaderItem(ColFromHost, new QStandardItem(i18n("From Hostname"))); m_roles = QStandardItemModel::roleNames(); m_roles[RoleJobId] = "jobId"; m_roles[RoleJobState] = "jobState"; m_roles[RoleJobName] = "jobName"; m_roles[RoleJobPages] = "jobPages"; m_roles[RoleJobSize] = "jobSize"; m_roles[RoleJobOwner] = "jobOwner"; m_roles[RoleJobCreatedAt] = "jobCreatedAt"; m_roles[RoleJobIconName] = "jobIconName"; m_roles[RoleJobCancelEnabled] = "jobCancelEnabled"; m_roles[RoleJobHoldEnabled] = "jobHoldEnabled"; m_roles[RoleJobReleaseEnabled] = "jobReleaseEnabled"; m_roles[RoleJobRestartEnabled] = "jobRestartEnabled"; m_roles[RoleJobPrinter] = "jobPrinter"; m_roles[RoleJobOriginatingHostName] = "jobFrom"; // This is emitted when a job change it's state connect(KCupsConnection::global(), &KCupsConnection::jobState, this, &JobModel::insertUpdateJob); // This is emitted when a job is created connect(KCupsConnection::global(), &KCupsConnection::jobCreated, this, &JobModel::insertUpdateJob); // This is emitted when a job is stopped connect(KCupsConnection::global(), &KCupsConnection::jobStopped, this, &JobModel::insertUpdateJob); // This is emitted when a job has it's config changed connect(KCupsConnection::global(), &KCupsConnection::jobConfigChanged, this, &JobModel::insertUpdateJob); // This is emitted when a job change it's progress connect(KCupsConnection::global(), &KCupsConnection::jobProgress, this, &JobModel::insertUpdateJob); // This is emitted when a printer is removed connect(KCupsConnection::global(), &KCupsConnection::jobCompleted, this, &JobModel::jobCompleted); connect(KCupsConnection::global(), &KCupsConnection::serverAudit, this, &JobModel::getJobs); connect(KCupsConnection::global(), &KCupsConnection::serverStarted, this, &JobModel::getJobs); connect(KCupsConnection::global(), &KCupsConnection::serverStopped, this, &JobModel::getJobs); connect(KCupsConnection::global(), &KCupsConnection::serverRestarted, this, &JobModel::getJobs); } void JobModel::setParentWId(WId parentId) { m_parentId = parentId; } void JobModel::init(const QString &destName) { m_destName = destName; // Get all jobs getJobs(); } void JobModel::hold(const QString &printerName, int jobId) { QPointer request = new KCupsRequest; request->holdJob(printerName, jobId); request->waitTillFinished(); if (request) { request->deleteLater(); } } void JobModel::release(const QString &printerName, int jobId) { QPointer request = new KCupsRequest; request->releaseJob(printerName, jobId); request->waitTillFinished(); if (request) { request->deleteLater(); } } void JobModel::cancel(const QString &printerName, int jobId) { QPointer request = new KCupsRequest; request->cancelJob(printerName, jobId); request->waitTillFinished(); if (request) { request->deleteLater(); } } void JobModel::move(const QString &printerName, int jobId, const QString &toPrinterName) { QPointer request = new KCupsRequest; request->moveJob(printerName, jobId, toPrinterName); request->waitTillFinished(); if (request) { request->deleteLater(); } } void JobModel::getJobs() { if (m_jobRequest) { return; } m_jobRequest = new KCupsRequest; connect(m_jobRequest, &KCupsRequest::finished, this, &JobModel::getJobFinished); const static QStringList attrs({ KCUPS_JOB_ID, KCUPS_JOB_NAME, KCUPS_JOB_K_OCTETS, KCUPS_JOB_K_OCTETS_PROCESSED, KCUPS_JOB_STATE, + KCUPS_JOB_STATE_REASONS, + KCUPS_JOB_HOLD_UNTIL, KCUPS_TIME_AT_COMPLETED, KCUPS_TIME_AT_CREATION, KCUPS_TIME_AT_PROCESSING, KCUPS_JOB_PRINTER_URI, KCUPS_JOB_ORIGINATING_USER_NAME, KCUPS_JOB_ORIGINATING_HOST_NAME, KCUPS_JOB_MEDIA_PROGRESS, KCUPS_JOB_MEDIA_SHEETS, KCUPS_JOB_MEDIA_SHEETS_COMPLETED, KCUPS_JOB_PRINTER_STATE_MESSAGE, KCUPS_JOB_PRESERVED }); m_jobRequest->getJobs(m_destName, false, m_whichjobs, attrs); m_processingJob.clear(); } void JobModel::getJobFinished(KCupsRequest *request) { if (request) { if (request->hasError()) { // clear the model after so that the proper widget can be shown clear(); } else { const KCupsJobs jobs = request->jobs(); qCDebug(LIBKCUPS) << jobs.size(); for (int i = 0; i < jobs.size(); ++i) { const KCupsJob job = jobs.at(i); if (job.state() == IPP_JOB_PROCESSING) { m_processingJob = job.name(); } // try to find the job row const int job_row = jobRow(job.id()); if (job_row == -1) { // not found, insert new one insertJob(i, job); } else { // update the job updateJob(job_row, job); if (job_row != i) { // found at wrong position // take it and insert on the right position const QList row = takeRow(job_row); insertRow(i, row); } } } // remove old printers // The above code starts from 0 and make sure // dest == modelIndex(x) and if it's not the // case it either inserts or moves it. // so any item > num_jobs can be safely deleted while (rowCount() > jobs.size()) { removeRow(rowCount() - 1); } } request->deleteLater(); } else { qCWarning(LIBKCUPS) << "Should not be called from a non KCupsRequest class" << sender(); } m_jobRequest = nullptr; } void JobModel::jobCompleted(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs, uint jobId, uint jobState, const QString &jobStateReasons, const QString &jobName, uint jobImpressionsCompleted) { // REALLY? all these parameters just to say foo was deleted?? Q_UNUSED(text) Q_UNUSED(printerUri) Q_UNUSED(printerName) Q_UNUSED(printerState) Q_UNUSED(printerStateReasons) Q_UNUSED(printerIsAcceptingJobs) Q_UNUSED(jobId) Q_UNUSED(jobState) Q_UNUSED(jobStateReasons) Q_UNUSED(jobName) Q_UNUSED(jobImpressionsCompleted) // We grab all jobs again getJobs(); } void JobModel::insertUpdateJob(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs, uint jobId, uint jobState, const QString &jobStateReasons, const QString &jobName, uint jobImpressionsCompleted) { // REALLY? all these parameters just to say foo was created?? Q_UNUSED(text) Q_UNUSED(printerUri) Q_UNUSED(printerName) Q_UNUSED(printerState) Q_UNUSED(printerStateReasons) Q_UNUSED(printerIsAcceptingJobs) Q_UNUSED(jobId) Q_UNUSED(jobState) Q_UNUSED(jobStateReasons) Q_UNUSED(jobName) Q_UNUSED(jobImpressionsCompleted) // We grab all jobs again getJobs(); } void JobModel::insertJob(int pos, const KCupsJob &job) { // insert the first column which has the job state and id QList row; ipp_jstate_e jobState = job.state(); auto statusItem = new QStandardItem(jobStatus(jobState)); statusItem->setData(jobState, RoleJobState); statusItem->setData(job.id(), RoleJobId); statusItem->setData(job.name(), RoleJobName); statusItem->setData(job.originatingUserName(), RoleJobOwner); statusItem->setData(job.originatingHostName(), RoleJobOriginatingHostName); QString size = KFormat().formatByteSize(job.size()); statusItem->setData(size, RoleJobSize); QString createdAt = QLocale().toString(job.createdAt()); statusItem->setData(createdAt, RoleJobCreatedAt); // TODO move the update code before the insert and reuse some code... statusItem->setData(KCupsJob::iconName(jobState), RoleJobIconName); statusItem->setData(KCupsJob::cancelEnabled(jobState), RoleJobCancelEnabled); statusItem->setData(KCupsJob::holdEnabled(jobState), RoleJobHoldEnabled); statusItem->setData(KCupsJob::releaseEnabled(jobState), RoleJobReleaseEnabled); statusItem->setData(job.reprintEnabled(), RoleJobRestartEnabled); QString pages = QString::number(job.pages()); if (job.processedPages()) { pages = QString::number(job.processedPages()) + QLatin1Char('/') + QString::number(job.processedPages()); } if (statusItem->data(RoleJobPages) != pages) { statusItem->setData(pages, RoleJobPages); } row << statusItem; for (int i = ColName; i < LastColumn; i++) { // adds all Items to the model row << new QStandardItem; } // insert the whole row insertRow(pos, row); // update the items updateJob(pos, job); } void JobModel::updateJob(int pos, const KCupsJob &job) { // Job Status & internal dataipp_jstate_e ipp_jstate_e jobState = job.state(); QStandardItem *colStatus = item(pos, ColStatus); if (colStatus->data(RoleJobState).toInt() != jobState) { colStatus->setText(jobStatus(jobState)); colStatus->setData(static_cast(jobState), RoleJobState); colStatus->setData(KCupsJob::iconName(jobState), RoleJobIconName); colStatus->setData(KCupsJob::cancelEnabled(jobState), RoleJobCancelEnabled); colStatus->setData(KCupsJob::holdEnabled(jobState), RoleJobHoldEnabled); colStatus->setData(KCupsJob::releaseEnabled(jobState), RoleJobReleaseEnabled); colStatus->setData(job.reprintEnabled(), RoleJobRestartEnabled); } const QString pages = job.processedPages() ? QString::number(job.processedPages()) + QLatin1Char('/') + QString::number(job.processedPages()) : QString::number(job.pages()); if (colStatus->data(RoleJobPages) != pages) { colStatus->setData(pages, RoleJobPages); } + colStatus->setData(job.authenticationRequired(), RoleJobAuthenticationRequired); + // internal dest name & column const QString destName = job.printer(); if (colStatus->data(RoleJobPrinter).toString() != destName) { colStatus->setData(destName, RoleJobPrinter); // Column job printer Name item(pos, ColPrinter)->setText(destName); } // job name const QString jobName = job.name(); if (item(pos, ColName)->text() != jobName) { colStatus->setData(jobName, RoleJobName); item(pos, ColName)->setText(jobName); } // owner of the job // try to get the full user name QString userString = job.originatingUserName(); const KUser user(userString); if (user.isValid() && !user.property(KUser::FullName).toString().isEmpty()) { userString = user.property(KUser::FullName).toString(); } // user name QStandardItem *colUser = item(pos, ColUser); if (colUser->text() != userString) { colUser->setText(userString); } // when it was created const QDateTime timeAtCreation = job.createdAt(); QStandardItem *colCreated = item(pos, ColCreated); if (colCreated->data(Qt::DisplayRole).toDateTime() != timeAtCreation) { colCreated->setData(timeAtCreation, Qt::DisplayRole); } // when it was completed const QDateTime completedAt = job.completedAt(); QStandardItem *colCompleted = item(pos, ColCompleted); if (colCompleted->data(Qt::DisplayRole).toDateTime() != completedAt) { if (!completedAt.isNull()) { colCompleted->setData(completedAt, Qt::DisplayRole); } else { // Clean the data might happen when the job is restarted colCompleted->setText(QString()); } } // job pages const int completedPages = job.processedPages(); QStandardItem *colPages = item(pos, ColPages); if (colPages->data(Qt::UserRole) != completedPages) { colPages->setData(completedPages, Qt::UserRole); colPages->setText(QString::number(completedPages)); } // when it was processed const QDateTime timeAtProcessing = job.processedAt(); QStandardItem *colProcessed = item(pos, ColProcessed); if (colProcessed->data(Qt::DisplayRole).toDateTime() != timeAtProcessing) { if (!timeAtProcessing.isNull()) { colProcessed->setData(timeAtProcessing, Qt::DisplayRole); } else { // Clean the data might happen when the job is restarted colCompleted->setText(QString()); } } int jobSize = job.size(); QStandardItem *colSize = item(pos, ColSize); if (colSize->data(Qt::UserRole) != jobSize) { colSize->setData(jobSize, Qt::UserRole); colSize->setText(KFormat().formatByteSize(jobSize)); } // job printer state message const QString stateMessage = job.stateMsg(); QStandardItem *colStatusMessage = item(pos, ColStatusMessage); if (colStatusMessage->text() != stateMessage) { colStatusMessage->setText(stateMessage); } // owner of the job // try to get the full user name const QString originatingHostName = job.originatingHostName(); QStandardItem *colFromHost = item(pos, ColFromHost); if (colFromHost->text() != originatingHostName) { colFromHost->setText(originatingHostName); } } QStringList JobModel::mimeTypes() const { return { QStringLiteral("application/x-cupsjobs") }; } Qt::DropActions JobModel::supportedDropActions() const { return Qt::MoveAction; } QMimeData* JobModel::mimeData(const QModelIndexList &indexes) const { auto mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); for (const QModelIndex &index : indexes) { if (index.isValid() && index.column() == 0) { // serialize the jobId and fromDestName stream << data(index, RoleJobId).toInt() << data(index, RoleJobPrinter).toString() << item(index.row(), ColName)->text(); } } mimeData->setData(QLatin1String("application/x-cupsjobs"), encodedData); return mimeData; } bool JobModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(row) Q_UNUSED(column) Q_UNUSED(parent) if (action == Qt::IgnoreAction) { return true; } if (!data->hasFormat(QLatin1String("application/x-cupsjobs"))) { return false; } QByteArray encodedData = data->data(QLatin1String("application/x-cupsjobs")); QDataStream stream(&encodedData, QIODevice::ReadOnly); bool ret = false; while (!stream.atEnd()) { QString fromDestName, displayName; int jobId; // get the jobid and the from dest name stream >> jobId >> fromDestName >> displayName; if (fromDestName == m_destName) { continue; } QPointer request = new KCupsRequest; request->moveJob(fromDestName, jobId, m_destName); request->waitTillFinished(); if (request) { if (request->hasError()) { // failed to move one job // we return here to avoid more password tries KMessageBox::detailedSorryWId(m_parentId, i18n("Failed to move '%1' to '%2'", displayName, m_destName), request->errorMsg(), i18n("Failed")); } request->deleteLater(); ret = !request->hasError(); } } return ret; } QHash JobModel::roleNames() const { return m_roles; } KCupsRequest* JobModel::modifyJob(int row, JobAction action, const QString &newDestName, const QModelIndex &parent) { Q_UNUSED(parent) if (row < 0 || row >= rowCount()) { qCWarning(LIBKCUPS) << "Row number is invalid:" << row; return 0; } QStandardItem *job = item(row, ColStatus); int jobId = job->data(RoleJobId).toInt(); QString destName = job->data(RoleJobPrinter).toString(); // ignore some jobs ipp_jstate_t state = static_cast(job->data(RoleJobState).toInt()); if ((state == IPP_JOB_HELD && action == Hold) || (state == IPP_JOB_CANCELED && action == Cancel) || (state != IPP_JOB_HELD && action == Release)) { return 0; } auto request = new KCupsRequest; switch (action) { case Cancel: request->cancelJob(destName, jobId); break; case Hold: request->holdJob(destName, jobId); break; case Release: request->releaseJob(destName, jobId); break; case Reprint: request->restartJob(destName, jobId); break; case Move: request->moveJob(destName, jobId, newDestName); break; default: qCWarning(LIBKCUPS) << "Unknown ACTION called!!!" << action; return 0; } return request; } int JobModel::jobRow(int jobId) { // find the position of the jobId inside the model for (int i = 0; i < rowCount(); i++) { if (jobId == item(i)->data(RoleJobId).toInt()) { return i; } } // -1 if not found return -1; } QString JobModel::jobStatus(ipp_jstate_e job_state) { switch (job_state) { case IPP_JOB_PENDING : return i18n("Pending"); case IPP_JOB_HELD : return i18n("On hold"); case IPP_JOB_PROCESSING : return QLatin1String("-"); case IPP_JOB_STOPPED : return i18n("Stopped"); case IPP_JOB_CANCELED : return i18n("Canceled"); case IPP_JOB_ABORTED : return i18n("Aborted"); case IPP_JOB_COMPLETED : return i18n("Completed"); } return QLatin1String("-"); } void JobModel::clear() { removeRows(0, rowCount()); } void JobModel::setWhichJobs(WhichJobs whichjobs) { switch (whichjobs) { case WhichActive: m_whichjobs = CUPS_WHICHJOBS_ACTIVE; break; case WhichCompleted: m_whichjobs = CUPS_WHICHJOBS_COMPLETED; break; case WhichAll: m_whichjobs = CUPS_WHICHJOBS_ALL; break; } getJobs(); } Qt::ItemFlags JobModel::flags(const QModelIndex &index) const { if (index.isValid()) { ipp_jstate_t state = static_cast(item(index.row(), ColStatus)->data(RoleJobState).toInt()); if (state == IPP_JOB_PENDING || state == IPP_JOB_PROCESSING) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } } return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; } QString JobModel::processingJob() const { return m_processingJob; } #include "moc_JobModel.cpp" diff --git a/libkcups/JobModel.h b/libkcups/JobModel.h index 059963f..f528332 100644 --- a/libkcups/JobModel.h +++ b/libkcups/JobModel.h @@ -1,150 +1,151 @@ /*************************************************************************** * Copyright (C) 2010 by Daniel Nicoletti * * dantti12@gmail.com * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef JOB_MODEL_H #define JOB_MODEL_H #include #include class KCupsJob; class KCupsRequest; class Q_DECL_EXPORT JobModel : public QStandardItemModel { Q_OBJECT Q_ENUMS(JobAction) Q_ENUMS(Role) Q_ENUMS(WhichJobs) public: enum Role { RoleJobId = Qt::UserRole + 2, RoleJobState, RoleJobName, RoleJobPages, RoleJobSize, RoleJobOwner, RoleJobCreatedAt, RoleJobIconName, RoleJobCancelEnabled, RoleJobHoldEnabled, RoleJobReleaseEnabled, RoleJobRestartEnabled, RoleJobPrinter, - RoleJobOriginatingHostName + RoleJobOriginatingHostName, + RoleJobAuthenticationRequired }; enum JobAction { Cancel, Hold, Release, Move, Reprint }; enum WhichJobs { WhichAll, WhichActive, WhichCompleted }; enum Columns { ColStatus = 0, ColName, ColUser, ColCreated, ColCompleted, ColPages, ColProcessed, ColSize, ColStatusMessage, ColPrinter, ColFromHost, LastColumn }; explicit JobModel(QObject *parent = nullptr); void setParentWId(WId parentId); Q_INVOKABLE void init(const QString &destName = QString()); Q_INVOKABLE void hold(const QString &printerName, int jobId); Q_INVOKABLE void release(const QString &printerName, int jobId); Q_INVOKABLE void cancel(const QString &printerName, int jobId); Q_INVOKABLE void move(const QString &printerName, int jobId, const QString &toPrinterName); QString processingJob() const; Qt::ItemFlags flags(const QModelIndex &index) const override; QStringList mimeTypes() const override; Qt::DropActions supportedDropActions() const override; QMimeData* mimeData(const QModelIndexList &indexes) const override; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; virtual QHash roleNames() const override; Q_INVOKABLE void setWhichJobs(WhichJobs whichjobs); KCupsRequest* modifyJob(int row, JobAction action, const QString &newDestName = QString(), const QModelIndex &parent = QModelIndex()); private slots: void getJobs(); void getJobFinished(KCupsRequest *request); void jobCompleted(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs, uint jobId, uint jobState, const QString &jobStateReasons, const QString &jobName, uint jobImpressionsCompleted); void insertUpdateJob(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs, uint jobId, uint jobState, const QString &jobStateReasons, const QString &jobName, uint jobImpressionsCompleted); private: int jobRow(int jobId); void insertJob(int pos, const KCupsJob &job); void updateJob(int pos, const KCupsJob &job); QString jobStatus(ipp_jstate_e job_state); void clear(); KCupsRequest *m_jobRequest = nullptr; QString m_destName; QString m_processingJob; QHash m_roles; int m_whichjobs = CUPS_WHICHJOBS_ACTIVE; WId m_parentId = 0; }; #endif // JOB_MODEL_H diff --git a/libkcups/KCupsConnection.h b/libkcups/KCupsConnection.h index a15d97b..8e3ae4f 100644 --- a/libkcups/KCupsConnection.h +++ b/libkcups/KCupsConnection.h @@ -1,405 +1,410 @@ /*************************************************************************** * Copyright (C) 2010-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef KCUPSCONNECTION_H #define KCUPSCONNECTION_H #include #include #include #include #include #include #include #include #include #define KCUPS_DEVICE_CLASS QLatin1String("device-class") #define KCUPS_DEVICE_ID QLatin1String("device-id") #define KCUPS_DEVICE_INFO QLatin1String("device-info") #define KCUPS_DEVICE_MAKE_AND_MODEL QLatin1String("device-make-and-model") #define KCUPS_DEVICE_LOCATION QLatin1String("device-location") #define KCUPS_DEVICE_URI QLatin1String("device-uri") #define KCUPS_PRINTER_NAME QLatin1String("printer-name") #define KCUPS_PRINTER_LOCATION QLatin1String("printer-location") #define KCUPS_PRINTER_INFO QLatin1String("printer-info") #define KCUPS_PRINTER_URI QLatin1String("printer-uri") #define KCUPS_PRINTER_MAKE_AND_MODEL QLatin1String("printer-make-and-model") #define KCUPS_PRINTER_STATE QLatin1String("printer-state") #define KCUPS_PRINTER_STATE_MESSAGE QLatin1String("printer-state-message") #define KCUPS_PRINTER_IS_SHARED QLatin1String("printer-is-shared") #define KCUPS_PRINTER_IS_ACCEPTING_JOBS QLatin1String("printer-is-accepting-jobs") #define KCUPS_PRINTER_TYPE QLatin1String("printer-type") #define KCUPS_PRINTER_TYPE_MASK QLatin1String("printer-type-mask") #define KCUPS_PRINTER_COMMANDS QLatin1String("printer-commands") #define KCUPS_PRINTER_URI_SUPPORTED QLatin1String("printer-uri-supported") #define KCUPS_PRINTER_ERROR_POLICY QLatin1String("printer-error-policy") #define KCUPS_PRINTER_ERROR_POLICY_SUPPORTED QLatin1String("printer-error-policy-supported") #define KCUPS_PRINTER_OP_POLICY QLatin1String("printer-op-policy") #define KCUPS_PRINTER_OP_POLICY_SUPPORTED QLatin1String("printer-op-policy-supported") #define KCUPS_MEMBER_URIS QLatin1String("member-uris") #define KCUPS_MEMBER_NAMES QLatin1String("member-names") #define KCUPS_MARKER_CHANGE_TIME QLatin1String("marker-change-time") #define KCUPS_MARKER_COLORS QLatin1String("marker-colors") #define KCUPS_MARKER_LEVELS QLatin1String("marker-levels") #define KCUPS_MARKER_HIGH_LEVELS "marker-high-levels" #define KCUPS_MARKER_LOW_LEVELS "marker-low-levels" #define KCUPS_MARKER_NAMES QLatin1String("marker-names") #define KCUPS_MARKER_TYPES QLatin1String("marker-types") #define KCUPS_MARKER_MESSAGE "marker-message" #define KCUPS_JOB_ID QLatin1String("job-id") #define KCUPS_JOB_NAME QLatin1String("job-name") #define KCUPS_JOB_K_OCTETS QLatin1String("job-k-octets") #define KCUPS_JOB_K_OCTETS_PROCESSED QLatin1String("job-k-octets-processed") #define KCUPS_JOB_PRINTER_URI QLatin1String("job-printer-uri") #define KCUPS_JOB_PRINTER_STATE_MESSAGE QLatin1String("job-printer-state-message") #define KCUPS_JOB_ORIGINATING_USER_NAME QLatin1String("job-originating-user-name") #define KCUPS_JOB_ORIGINATING_HOST_NAME QLatin1String("job-originating-host-name") #define KCUPS_JOB_MEDIA_PROGRESS QLatin1String("job-media-progress") #define KCUPS_JOB_MEDIA_SHEETS QLatin1String("job-media-sheets") #define KCUPS_JOB_MEDIA_SHEETS_COMPLETED QLatin1String("job-media-sheets-completed") #define KCUPS_JOB_PRESERVED QLatin1String("job-preserved") #define KCUPS_JOB_STATE QLatin1String("job-state") +#define KCUPS_JOB_STATE_REASONS QLatin1String("job-state-reasons") +#define KCUPS_JOB_HOLD_UNTIL QLatin1String("job-hold-until") #define KCUPS_JOB_SHEETS_DEFAULT QLatin1String("job-sheets-default") #define KCUPS_JOB_SHEETS_SUPPORTED QLatin1String("job-sheets-supported") #define KCUPS_JOB_SHEETS_DEFAULT QLatin1String("job-sheets-default") #define KCUPS_JOB_SHEETS_SUPPORTED QLatin1String("job-sheets-supported") #define KCUPS_MY_JOBS QLatin1String("my-jobs") #define KCUPS_WHICH_JOBS QLatin1String("which-jobs") #define KCUPS_TIME_AT_COMPLETED QLatin1String("time-at-completed") #define KCUPS_TIME_AT_CREATION QLatin1String("time-at-creation") #define KCUPS_TIME_AT_PROCESSING QLatin1String("time-at-processing") #define KCUPS_REQUESTED_ATTRIBUTES QLatin1String("requested-attributes") #define KCUPS_REQUESTING_USER_NAME QLatin1String("requesting-user-name") #define KCUPS_REQUESTING_USER_NAME_ALLOWED QLatin1String("requesting-user-name-allowed") #define KCUPS_REQUESTING_USER_NAME_DENIED QLatin1String("requesting-user-name-denied") #define KCUPS_PPD_MAKE_AND_MODEL QLatin1String("ppd-make-and-model") #define KCUPS_NOTIFY_EVENTS QLatin1String("notify-events") #define KCUPS_NOTIFY_PULL_METHOD QLatin1String("notify-pull-method") #define KCUPS_NOTIFY_RECIPIENT_URI QLatin1String("notify-recipient-uri") #define KCUPS_NOTIFY_LEASE_DURATION QLatin1String("notify-lease-duration") #define KCUPS_NOTIFY_SUBSCRIPTION_ID QLatin1String("notify-subscription-id") +#define KCUPS_AUTH_INFO QLatin1String("auth-info") +#define KCUPS_AUTH_INFO_REQUIRED QLatin1String("auth-info-required") + typedef QList ReturnArguments; class KIppRequest; class KCupsPasswordDialog; class Q_DECL_EXPORT KCupsConnection : public QThread { Q_OBJECT public: /** * This is the main Cups class @author Daniel Nicoletti * * By calling KCupsConnection::global() you have access to it. * Due to cups architecture, this class has to live on a * separate thread so we avoid blocking the user interface when * the cups call blocks. * * It is IMPORTANT that we do not create several thread * for each cups request, doing so is a valid but breaks our * authentication. We could tho store the user information an * set the user/password every time it was needed. But I am not * sure this is safe. * * Extending this means either adding methods to the KCupsRequest * class which will move to this thread and then run. */ static KCupsConnection* global(); /** * @brief KCupsConnection * @param parent * * This is the default constructor that connects to the default server * If you don't have any special reason for creating a connection * on your own consider calling global() */ explicit KCupsConnection(QObject *parent = nullptr); explicit KCupsConnection(const QUrl &server, QObject *parent = nullptr); ~KCupsConnection() override; void setPasswordMainWindow(WId mainwindow); Q_SIGNALS: /** * emitted when "server-started" is registered */ void serverStarted(const QString &text); /** * emitted when "server-stopped" is registered */ void serverStopped(const QString &text); /** * emitted when "server-restarted" is registered */ void serverRestarted(const QString &text); /** * emitted when "server-audit" is registered */ void serverAudit(const QString &text); /** * emitted when "printer-added" is registered */ void printerAdded(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); /** * emitted when "printer-modified" is registered */ void printerModified(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); /** * emitted when "printer-deleted" is registered */ void printerDeleted(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); /** * emitted when "printer-state-changed" is registered */ void printerStateChanged(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); /** * emitted when "printer-stopped" is registered */ void printerStopped(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); /** * emitted when "printer-restarted" is registered */ void printerRestarted(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); /** * emitted when "printer-shutdown" is registered */ void printerShutdown(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); /** * emitted when "printer-media-changed" is registered */ void printerMediaChanged(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); /** * emitted when "printer-finishings-changed" is registered */ void printerFinishingsChanged(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); /** * emitted when "job-state-changed" is registered */ void jobState(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs, uint jobId, uint jobState, const QString &jobStateReasons, const QString &jobName, uint jobImpressionsCompleted); /** * emitted when "job-created" is registered */ void jobCreated(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs, uint jobId, uint jobState, const QString &jobStateReasons, const QString &jobName, uint jobImpressionsCompleted); /** * emitted when "job-stopped" is registered */ void jobStopped(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs, uint jobId, uint jobState, const QString &jobStateReasons, const QString &jobName, uint jobImpressionsCompleted); /** * emitted when "job-config-changed" is registered */ void jobConfigChanged(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs, uint jobId, uint jobState, const QString &jobStateReasons, const QString &jobName, uint jobImpressionsCompleted); /** * emitted when "job-progress" is registered */ void jobProgress(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs, uint jobId, uint jobState, const QString &jobStateReasons, const QString &jobName, uint jobImpressionsCompleted); /** * emitted when "job-completed" is registered */ void jobCompleted(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs, uint jobId, uint jobState, const QString &jobStateReasons, const QString &jobName, uint jobImpressionsCompleted); void rhPrinterAdded(const QString &queueName); void rhPrinterRemoved(const QString &queueName); void rhQueueChanged(const QString &queueName); void rhJobQueuedLocal(const QString &queueName, uint jobId, const QString &jobOwner); void rhJobStartedLocal(const QString &queueName, uint jobId, const QString &jobOwner); protected: friend class KCupsRequest; virtual void run() override; bool readyToStart(); bool retry(const char *resource, int operation) const; ReturnArguments request(const KIppRequest &request, ipp_tag_t groupTag = IPP_TAG_ZERO) const; private slots: void updateSubscription(); void renewDBusSubscription(); void cancelDBusSubscription(); protected: virtual void connectNotify(const QMetaMethod & signal) override; virtual void disconnectNotify(const QMetaMethod & signal) override; QString eventForSignal(const QMetaMethod & signal) const; private: void init(); int renewDBusSubscription(int subscriptionId, int leaseDuration, const QStringList &events = QStringList()); void notifierConnect(const QString &signal, QObject *receiver, const char *slot); static ReturnArguments parseIPPVars(ipp_t *response, ipp_tag_t group_tag); static QVariant ippAttrToVariant(ipp_attribute_t *attr); static KCupsConnection* m_instance; bool m_inited = false; KCupsPasswordDialog *m_passwordDialog; QUrl m_serverUrl; QTimer *m_subscriptionTimer; QTimer *m_renewTimer; QStringList m_connectedEvents; //note this updated in another thread. Always guard with m_mutex QStringList m_requestedDBusEvents; int m_subscriptionId = -1; QMutex m_mutex; }; #endif // KCUPSCONNECTION_H diff --git a/libkcups/KCupsJob.cpp b/libkcups/KCupsJob.cpp index 5ed2e8f..bbfe115 100644 --- a/libkcups/KCupsJob.cpp +++ b/libkcups/KCupsJob.cpp @@ -1,210 +1,225 @@ /*************************************************************************** * Copyright (C) 2010-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "KCupsJob.h" #include "Debug.h" KCupsJob::KCupsJob() : m_jobId(0) { } KCupsJob::KCupsJob(int jobId, const QString &printer) : m_jobId(jobId), m_printer(printer) { m_arguments[KCUPS_JOB_ID] = QString::number(jobId); } KCupsJob::KCupsJob(const QVariantHash &arguments) : m_arguments(arguments) { m_jobId = arguments[KCUPS_JOB_ID].toInt(); m_printer = arguments[KCUPS_JOB_PRINTER_URI].toString().section(QLatin1Char('/'), -1); } int KCupsJob::id() const { return m_jobId; } QString KCupsJob::idStr() const { return m_arguments[KCUPS_JOB_ID].toString(); } QString KCupsJob::name() const { return m_arguments[KCUPS_JOB_NAME].toString(); } QString KCupsJob::originatingUserName() const { return m_arguments[KCUPS_JOB_ORIGINATING_USER_NAME].toString(); } QString KCupsJob::originatingHostName() const { return m_arguments[KCUPS_JOB_ORIGINATING_HOST_NAME].toString(); } QString KCupsJob::printer() const { return m_printer; } QDateTime KCupsJob::createdAt() const { QDateTime ret; const auto it = m_arguments.constFind(KCUPS_TIME_AT_CREATION); if (it != m_arguments.constEnd()) { ret.fromMSecsSinceEpoch(it.value().toInt() * 1000); } return ret; } QDateTime KCupsJob::completedAt() const { QDateTime ret; const auto it = m_arguments.constFind(KCUPS_TIME_AT_COMPLETED); if (it != m_arguments.constEnd()) { ret.fromMSecsSinceEpoch(it.value().toInt() * 1000); } return ret; } QDateTime KCupsJob::processedAt() const { QDateTime ret; const auto it = m_arguments.constFind(KCUPS_TIME_AT_PROCESSING); if (it != m_arguments.constEnd()) { ret.fromMSecsSinceEpoch(it.value().toInt() * 1000); } return ret; } int KCupsJob::pages() const { return m_arguments[KCUPS_JOB_MEDIA_SHEETS].toInt(); } int KCupsJob::processedPages() const { return m_arguments[KCUPS_JOB_MEDIA_SHEETS_COMPLETED].toInt(); } int KCupsJob::size() const { int jobKOctets = m_arguments[KCUPS_JOB_K_OCTETS].toInt(); jobKOctets *= 1024; // transform it to bytes return jobKOctets; } bool KCupsJob::preserved() const { return m_arguments[KCUPS_JOB_PRESERVED].toInt(); } QString KCupsJob::iconName(ipp_jstate_t state) { QString ret; switch (state){ case IPP_JOB_PENDING: ret = QLatin1String("chronometer"); break; case IPP_JOB_HELD: ret = QLatin1String("media-playback-pause"); break; case IPP_JOB_PROCESSING: ret = QLatin1String("draw-arrow-forward"); break; case IPP_JOB_STOPPED: ret = QLatin1String("draw-rectangle"); break; case IPP_JOB_CANCELED: ret = QLatin1String("archive-remove"); break; case IPP_JOB_ABORTED: ret = QLatin1String("task-attempt"); break; case IPP_JOB_COMPLETED: ret = QLatin1String("task-complete"); break; default: ret = QLatin1String("unknown"); } return ret; } ipp_jstate_t KCupsJob::state() const { return static_cast(m_arguments[KCUPS_JOB_STATE].toUInt()); } QString KCupsJob::stateMsg() const { return m_arguments[KCUPS_JOB_PRINTER_STATE_MESSAGE].toString(); } +QString KCupsJob::stateReason() const +{ + return m_arguments[KCUPS_JOB_STATE_REASONS].toString(); +} + +QString KCupsJob::holdUntil() const +{ + return m_arguments[KCUPS_JOB_HOLD_UNTIL].toString(); +} + +bool KCupsJob::authenticationRequired() const +{ + return stateReason() == QStringLiteral("cups-held-for-authentication") || holdUntil() == QStringLiteral("auth-info-required"); +} + bool KCupsJob::reprintEnabled() const { if (state() >= IPP_JOB_STOPPED && preserved()) { return true; } return false; } bool KCupsJob::cancelEnabled(ipp_jstate_t state) { switch (state) { case IPP_JOB_CANCELED: case IPP_JOB_COMPLETED: case IPP_JOB_ABORTED: return false; default: return true; } } bool KCupsJob::holdEnabled(ipp_jstate_t state) { switch (state) { case IPP_JOB_CANCELED: case IPP_JOB_COMPLETED: case IPP_JOB_ABORTED: case IPP_JOB_HELD: case IPP_JOB_STOPPED: return false; default: return true; } } bool KCupsJob::releaseEnabled(ipp_jstate_t state) { switch (state) { case IPP_JOB_HELD : case IPP_JOB_STOPPED : return true; default: return false; } } diff --git a/libkcups/KCupsJob.h b/libkcups/KCupsJob.h index bc07065..932b5f6 100644 --- a/libkcups/KCupsJob.h +++ b/libkcups/KCupsJob.h @@ -1,75 +1,78 @@ /*************************************************************************** * Copyright (C) 2010-2012 by Daniel Nicoletti * * dantti12@gmail.com * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef KCUPSJOB_H #define KCUPSJOB_H #include #include #include class Q_DECL_EXPORT KCupsJob { Q_ENUMS(Attribute) public: KCupsJob(); KCupsJob(int jobId, const QString &printer); int id() const; QString idStr() const; QString name() const; QString originatingUserName() const; QString originatingHostName() const; QString printer() const; QDateTime createdAt() const; QDateTime completedAt() const; QDateTime processedAt() const; int pages() const; int processedPages() const; int size() const; bool preserved() const; static QString iconName(ipp_jstate_t state); ipp_jstate_t state() const; QString stateMsg() const; + QString stateReason() const; + QString holdUntil() const; + bool authenticationRequired() const; bool reprintEnabled() const; static bool cancelEnabled(ipp_jstate_t state); static bool holdEnabled(ipp_jstate_t state); static bool releaseEnabled(ipp_jstate_t state); protected: KCupsJob(const QVariantHash &arguments); private: friend class KCupsRequest; int m_jobId; QString m_printer; QVariantHash m_arguments; }; typedef QList KCupsJobs; Q_DECLARE_METATYPE(KCupsJobs) Q_DECLARE_METATYPE(KCupsJob) #endif // KCUPSJOB_H diff --git a/libkcups/KCupsPrinter.cpp b/libkcups/KCupsPrinter.cpp index 716900e..b73c118 100644 --- a/libkcups/KCupsPrinter.cpp +++ b/libkcups/KCupsPrinter.cpp @@ -1,196 +1,201 @@ /*************************************************************************** * Copyright (C) 2010-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "KCupsPrinter.h" KCupsPrinter::KCupsPrinter() : m_isClass(false) { } KCupsPrinter::KCupsPrinter(const QString &printer, bool isClass) : m_printer(printer), m_isClass(isClass) { } KCupsPrinter::KCupsPrinter(const QVariantHash &arguments) : m_arguments(arguments) { m_printer = arguments[QLatin1String(KCUPS_PRINTER_NAME)].toString(); m_isClass = arguments[QLatin1String(KCUPS_PRINTER_TYPE)].toInt() & CUPS_PRINTER_CLASS; } QString KCupsPrinter::name() const { return m_printer; } bool KCupsPrinter::isClass() const { return m_isClass; } bool KCupsPrinter::isDefault() const { return m_arguments[QLatin1String(KCUPS_PRINTER_TYPE)].toUInt() & CUPS_PRINTER_DEFAULT; } bool KCupsPrinter::isShared() const { return m_arguments[QLatin1String(KCUPS_PRINTER_IS_SHARED)].toBool(); } bool KCupsPrinter::isAcceptingJobs() const { return m_arguments[QLatin1String(KCUPS_PRINTER_IS_ACCEPTING_JOBS)].toBool(); } cups_ptype_e KCupsPrinter::type() const { return static_cast(m_arguments[QLatin1String(KCUPS_PRINTER_TYPE)].toUInt()); } QString KCupsPrinter::location() const { return m_arguments[QLatin1String(KCUPS_PRINTER_LOCATION)].toString(); } QString KCupsPrinter::info() const { const QString printerInfo = m_arguments[QLatin1String(KCUPS_PRINTER_INFO)].toString(); if (printerInfo.isEmpty()) { return name(); } return printerInfo; } QString KCupsPrinter::makeAndModel() const { return m_arguments[QLatin1String(KCUPS_PRINTER_MAKE_AND_MODEL)].toString(); } QStringList KCupsPrinter::commands() const { return m_arguments[QLatin1String(KCUPS_PRINTER_COMMANDS)].toStringList(); } QStringList KCupsPrinter::memberNames() const { return m_arguments[QLatin1String(KCUPS_MEMBER_NAMES)].toStringList(); } QString KCupsPrinter::deviceUri() const { return m_arguments[QLatin1String(KCUPS_DEVICE_URI)].toString(); } QStringList KCupsPrinter::errorPolicy() const { return m_arguments[QLatin1String(KCUPS_PRINTER_ERROR_POLICY)].toStringList(); } QStringList KCupsPrinter::errorPolicySupported() const { return m_arguments[QLatin1String(KCUPS_PRINTER_ERROR_POLICY_SUPPORTED)].toStringList(); } QStringList KCupsPrinter::opPolicy() const { return m_arguments[QLatin1String(KCUPS_PRINTER_OP_POLICY)].toStringList(); } QStringList KCupsPrinter::opPolicySupported() const { return m_arguments[QLatin1String(KCUPS_PRINTER_OP_POLICY_SUPPORTED)].toStringList(); } QStringList KCupsPrinter::jobSheetsDefault() const { return m_arguments[QLatin1String(KCUPS_JOB_SHEETS_DEFAULT)].toStringList(); } QStringList KCupsPrinter::jobSheetsSupported() const { return m_arguments[QLatin1String(KCUPS_JOB_SHEETS_SUPPORTED)].toStringList(); } QStringList KCupsPrinter::requestingUserNameAllowed() const { return m_arguments[QLatin1String(KCUPS_REQUESTING_USER_NAME_ALLOWED)].toStringList(); } QStringList KCupsPrinter::requestingUserNameDenied() const { return m_arguments[QLatin1String(KCUPS_REQUESTING_USER_NAME_DENIED)].toStringList(); } +QStringList KCupsPrinter::authInfoRequired() const +{ + return m_arguments[QLatin1String(KCUPS_AUTH_INFO_REQUIRED)].toStringList(); +} + QString KCupsPrinter::uriSupported() const { return m_arguments[QLatin1String(KCUPS_PRINTER_URI_SUPPORTED)].toString(); } KCupsPrinter::Status KCupsPrinter::state() const { return static_cast(m_arguments[QLatin1String(KCUPS_PRINTER_STATE)].toUInt()); } QString KCupsPrinter::stateMsg() const { return m_arguments[QLatin1String(KCUPS_PRINTER_STATE_MESSAGE)].toString(); } int KCupsPrinter::markerChangeTime() const { return m_arguments[QLatin1String(KCUPS_MARKER_CHANGE_TIME)].toInt(); } QVariant KCupsPrinter::argument(const QString &name) const { return m_arguments.value(name); } QIcon KCupsPrinter::icon() const { return icon(type()); } QIcon KCupsPrinter::icon(cups_ptype_e type) { return QIcon::fromTheme(iconName(type)); } QString KCupsPrinter::iconName() const { return iconName(type()); } QString KCupsPrinter::iconName(cups_ptype_e type) { // TODO get the ppd or something to get the real printer icon if (!(type & CUPS_PRINTER_COLOR)) { // If the printer is not color it is probably a laser one return QStringLiteral("printer-laser"); } else if (type & CUPS_PRINTER_SCANNER) { return QStringLiteral("scanner"); } else { return QStringLiteral("printer"); } } diff --git a/libkcups/KCupsPrinter.h b/libkcups/KCupsPrinter.h index ba82554..25c0b84 100644 --- a/libkcups/KCupsPrinter.h +++ b/libkcups/KCupsPrinter.h @@ -1,92 +1,93 @@ /*************************************************************************** * Copyright (C) 2010-2012 by Daniel Nicoletti * * dantti12@gmail.com * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef KCUPSPRINTER_H #define KCUPSPRINTER_H #include #include #include class Q_DECL_EXPORT KCupsPrinter { Q_ENUMS(Attribute) public: typedef enum { Idle = 3, Printing, Stopped } Status; KCupsPrinter(); explicit KCupsPrinter(const QString &printer, bool isClass = false); QString name() const; bool isClass() const; bool isDefault() const; bool isShared() const; bool isAcceptingJobs() const; cups_ptype_e type() const; QString location() const; QString info() const; QString makeAndModel() const; QStringList commands() const; QStringList memberNames() const; QString deviceUri() const; QStringList errorPolicy() const; QStringList errorPolicySupported() const; QStringList opPolicy() const; QStringList opPolicySupported() const; QStringList jobSheetsDefault() const; QStringList jobSheetsSupported() const; QStringList requestingUserNameAllowed() const; QStringList requestingUserNameDenied() const; + QStringList authInfoRequired() const; QString uriSupported() const; Status state() const; QString stateMsg() const; int markerChangeTime() const; QVariant argument(const QString &name) const; /** * Requires enum PrinterType to work properly * */ QIcon icon() const; static QIcon icon(cups_ptype_e type); QString iconName() const; static QString iconName(cups_ptype_e type); protected: KCupsPrinter(const QVariantHash &arguments); private: friend class KCupsRequest; QString m_printer; bool m_isClass; QVariantHash m_arguments; }; typedef QList KCupsPrinters; Q_DECLARE_METATYPE(KCupsPrinters) Q_DECLARE_METATYPE(KCupsPrinter) #endif // KCUPSPRINTER_H diff --git a/libkcups/KCupsRequest.cpp b/libkcups/KCupsRequest.cpp index 542e48f..490ce79 100644 --- a/libkcups/KCupsRequest.cpp +++ b/libkcups/KCupsRequest.cpp @@ -1,669 +1,679 @@ /*************************************************************************** * Copyright (C) 2010-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "KCupsRequest.h" #include "Debug.h" #include "KIppRequest.h" #include "KCupsJob.h" #include "KCupsPrinter.h" #include #include #include #define CUPS_DATADIR QLatin1String("/usr/share/cups") KCupsRequest::KCupsRequest(KCupsConnection *connection) : m_connection(connection) { // If no connection was specified use default one if (m_connection == nullptr) { m_connection = KCupsConnection::global(); } connect(this, &KCupsRequest::finished, &m_loop, &QEventLoop::quit); } QString KCupsRequest::serverError() const { switch (error()) { case IPP_SERVICE_UNAVAILABLE: return i18n("Print service is unavailable"); case IPP_NOT_FOUND : return i18n("Not found"); default : // In this case we don't want to map all enums qCWarning(LIBKCUPS) << "status unrecognised: " << error(); return QString::fromUtf8(ippErrorString(error())); } } void KCupsRequest::getPPDS(const QString &make) { if (m_connection->readyToStart()) { KIppRequest request(CUPS_GET_PPDS, QLatin1String("/")); if (!make.isEmpty()) { request.addString(IPP_TAG_PRINTER, IPP_TAG_TEXT, KCUPS_PPD_MAKE_AND_MODEL, make); } m_ppds = m_connection->request(request, IPP_TAG_PRINTER); setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("getPPDS", make); } } static void choose_device_cb(const char *device_class, /* I - Class */ const char *device_id, /* I - 1284 device ID */ const char *device_info, /* I - Description */ const char *device_make_and_model, /* I - Make and model */ const char *device_uri, /* I - Device URI */ const char *device_location, /* I - Location */ void *user_data) /* I - Result object */ { /* * Add the device to the array... */ auto request = static_cast(user_data); QMetaObject::invokeMethod(request, "device", Qt::QueuedConnection, Q_ARG(QString, QString::fromUtf8(device_class)), Q_ARG(QString, QString::fromUtf8(device_id)), Q_ARG(QString, QString::fromUtf8(device_info)), Q_ARG(QString, QString::fromUtf8(device_make_and_model)), Q_ARG(QString, QString::fromUtf8(device_uri)), Q_ARG(QString, QString::fromUtf8(device_location))); } void KCupsRequest::getDevices(int timeout) { getDevices(timeout, QStringList(), QStringList()); } void KCupsRequest::getDevices(int timeout, QStringList includeSchemes, QStringList excludeSchemes) { if (m_connection->readyToStart()) { do { const char *include; if (includeSchemes.isEmpty()) { include = CUPS_INCLUDE_ALL; } else { include = qUtf8Printable(includeSchemes.join(QLatin1String(","))); } const char *exclude; if (excludeSchemes.isEmpty()) { exclude = CUPS_EXCLUDE_NONE; } else { exclude = qUtf8Printable(excludeSchemes.join(QLatin1String(","))); } // Scan for devices for "timeout" seconds cupsGetDevices(CUPS_HTTP_DEFAULT, timeout, include, exclude, (cups_device_cb_t) choose_device_cb, this); } while (m_connection->retry("/admin/", CUPS_GET_DEVICES)); setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(true); } else { invokeMethod("getDevices", timeout, includeSchemes, excludeSchemes); } } // THIS function can get the default server dest through the // "printer-is-default" attribute BUT it does not get user // defined default printer, see cupsGetDefault() on www.cups.org for details void KCupsRequest::getPrinters(QStringList attributes, int mask) { if (m_connection->readyToStart()) { KIppRequest request(CUPS_GET_PRINTERS, QLatin1String("/")); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, KCUPS_PRINTER_TYPE, CUPS_PRINTER_LOCAL); if (!attributes.isEmpty()) { request.addStringList(IPP_TAG_OPERATION, IPP_TAG_KEYWORD, KCUPS_REQUESTED_ATTRIBUTES, attributes); } if (mask != -1) { request.addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, KCUPS_PRINTER_TYPE_MASK, mask); } const ReturnArguments ret = m_connection->request(request, IPP_TAG_PRINTER); for (const QVariantHash &arguments : ret) { m_printers << KCupsPrinter(arguments); } setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("getPrinters", QVariant::fromValue(attributes), mask); } } void KCupsRequest::getPrinterAttributes(const QString &printerName, bool isClass, QStringList attributes) { if (m_connection->readyToStart()) { KIppRequest request(IPP_GET_PRINTER_ATTRIBUTES, QLatin1String("/")); request.addPrinterUri(printerName, isClass); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, QLatin1String(KCUPS_PRINTER_TYPE), CUPS_PRINTER_LOCAL); request.addStringList(IPP_TAG_OPERATION, IPP_TAG_KEYWORD, QLatin1String(KCUPS_REQUESTED_ATTRIBUTES), attributes); const ReturnArguments ret = m_connection->request(request, IPP_TAG_PRINTER); for (const QVariantHash &arguments : ret) { // Inject the printer name back to the arguments hash QVariantHash args = arguments; args[KCUPS_PRINTER_NAME] = printerName; m_printers << KCupsPrinter(args); } setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("getPrinterAttributes", printerName, isClass, QVariant::fromValue(attributes)); } } void KCupsRequest::getJobs(const QString &printerName, bool myJobs, int whichJobs, QStringList attributes) { if (m_connection->readyToStart()) { KIppRequest request(IPP_GET_JOBS, QLatin1String("/")); // printer-uri makes the Name of the Job and owner came blank lol request.addPrinterUri(printerName, false); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, KCUPS_PRINTER_TYPE, CUPS_PRINTER_LOCAL); request.addStringList(IPP_TAG_OPERATION, IPP_TAG_KEYWORD, KCUPS_REQUESTED_ATTRIBUTES, attributes); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, KCUPS_MY_JOBS, myJobs); if (whichJobs == CUPS_WHICHJOBS_COMPLETED) { request.addString(IPP_TAG_OPERATION, IPP_TAG_KEYWORD, KCUPS_WHICH_JOBS, QLatin1String("completed")); } else if (whichJobs == CUPS_WHICHJOBS_ALL) { request.addString(IPP_TAG_OPERATION, IPP_TAG_KEYWORD, KCUPS_WHICH_JOBS, QLatin1String("all")); } const ReturnArguments ret = m_connection->request(request, IPP_TAG_JOB); for (const QVariantHash &arguments : ret) { m_jobs << KCupsJob(arguments); } setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("getJobs", printerName, myJobs, whichJobs, QVariant::fromValue(attributes)); } } void KCupsRequest::getJobAttributes(int jobId, const QString &printerUri, QStringList attributes) { if (m_connection->readyToStart()) { KIppRequest request(IPP_GET_JOB_ATTRIBUTES, QLatin1String("/")); request.addString(IPP_TAG_OPERATION, IPP_TAG_URI, KCUPS_PRINTER_URI, printerUri); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, KCUPS_PRINTER_TYPE, CUPS_PRINTER_LOCAL); request.addStringList(IPP_TAG_OPERATION, IPP_TAG_KEYWORD, KCUPS_REQUESTED_ATTRIBUTES, attributes); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_JOB_ID, jobId); const ReturnArguments ret = m_connection->request(request, IPP_TAG_PRINTER); for (const QVariantHash &arguments : ret) { m_jobs << KCupsJob(arguments); } setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("getJobAttributes", jobId, printerUri, QVariant::fromValue(attributes)); } } void KCupsRequest::getServerSettings() { if (m_connection->readyToStart()) { do { int num_settings; cups_option_t *settings; QVariantHash arguments; int ret = cupsAdminGetServerSettings(CUPS_HTTP_DEFAULT, &num_settings, &settings); for (int i = 0; i < num_settings; ++i) { QString name = QString::fromUtf8(settings[i].name); QString value = QString::fromUtf8(settings[i].value); arguments[name] = value; } cupsFreeOptions(num_settings, settings); if (ret) { setError(HTTP_OK, IPP_OK, QString()); } else { setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); } m_server = KCupsServer(arguments); } while (m_connection->retry("/admin/", -1)); setFinished(); } else { invokeMethod("getServerSettings"); } } void KCupsRequest::getPrinterPPD(const QString &printerName) { if (m_connection->readyToStart()) { do { const char *filename; filename = cupsGetPPD2(CUPS_HTTP_DEFAULT, qUtf8Printable(printerName)); qCDebug(LIBKCUPS) << filename; m_ppdFile = QString::fromUtf8(filename); qCDebug(LIBKCUPS) << m_ppdFile; } while (m_connection->retry("/", CUPS_GET_PPD)); setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("getPrinterPPD", printerName); } } void KCupsRequest::setServerSettings(const KCupsServer &server) { if (m_connection->readyToStart()) { do { QVariantHash args = server.arguments(); int num_settings = 0; cups_option_t *settings; QVariantHash::const_iterator i = args.constBegin(); while (i != args.constEnd()) { num_settings = cupsAddOption(qUtf8Printable(i.key()), qUtf8Printable(i.value().toString()), num_settings, &settings); ++i; } cupsAdminSetServerSettings(CUPS_HTTP_DEFAULT, num_settings, settings); cupsFreeOptions(num_settings, settings); } while (m_connection->retry("/admin/", -1)); setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("setServerSettings", QVariant::fromValue(server)); } } void KCupsRequest::addOrModifyPrinter(const QString &printerName, const QVariantHash &attributes, const QString &filename) { KIppRequest request(CUPS_ADD_MODIFY_PRINTER, QLatin1String("/admin/"), filename); request.addPrinterUri(printerName); request.addVariantValues(attributes); process(request); } void KCupsRequest::addOrModifyClass(const QString &printerName, const QVariantHash &attributes) { KIppRequest request(CUPS_ADD_MODIFY_CLASS, QLatin1String("/admin/")); request.addPrinterUri(printerName, true); request.addVariantValues(attributes); process(request); } void KCupsRequest::setShared(const QString &printerName, bool isClass, bool shared) { KIppRequest request(isClass ? CUPS_ADD_MODIFY_CLASS : CUPS_ADD_MODIFY_PRINTER, QLatin1String("/admin/")); request.addPrinterUri(printerName, isClass); request.addBoolean(IPP_TAG_OPERATION, KCUPS_PRINTER_IS_SHARED, shared); process(request); } void KCupsRequest::pausePrinter(const QString &printerName) { KIppRequest request(IPP_PAUSE_PRINTER, QLatin1String("/admin/")); request.addPrinterUri(printerName); process(request); } void KCupsRequest::resumePrinter(const QString &printerName) { KIppRequest request(IPP_RESUME_PRINTER, QLatin1String("/admin/")); request.addPrinterUri(printerName); process(request); } void KCupsRequest::rejectJobs(const QString &printerName) { KIppRequest request(CUPS_REJECT_JOBS, QLatin1String("/admin/")); request.addPrinterUri(printerName); process(request); } void KCupsRequest::acceptJobs(const QString &printerName) { KIppRequest request(CUPS_ACCEPT_JOBS, QLatin1String("/admin/")); request.addPrinterUri(printerName); process(request); } void KCupsRequest::setDefaultPrinter(const QString &printerName) { KIppRequest request(CUPS_SET_DEFAULT, QLatin1String("/admin/")); request.addPrinterUri(printerName); process(request); } void KCupsRequest::deletePrinter(const QString &printerName) { KIppRequest request(CUPS_DELETE_PRINTER, QLatin1String("/admin/")); request.addPrinterUri(printerName); process(request); } void KCupsRequest::printTestPage(const QString &printerName, bool isClass) { QString resource; /* POST resource path */ QString filename; /* Test page filename */ QString datadir; /* CUPS_DATADIR env var */ /* * Locate the test page file... */ datadir = QString::fromUtf8(qgetenv("CUPS_DATADIR")); if (datadir.isEmpty()) { datadir = CUPS_DATADIR; } filename = datadir % QLatin1String("/data/testprint"); /* * Point to the printer/class... */ if (isClass) { resource = QLatin1String("/classes/") + printerName; } else { resource = QLatin1String("/printers/") + printerName; } KIppRequest request(IPP_PRINT_JOB, resource, filename); request.addPrinterUri(printerName); request.addString(IPP_TAG_OPERATION, IPP_TAG_NAME, KCUPS_JOB_NAME, i18n("Test Page")); process(request); } void KCupsRequest::printCommand(const QString &printerName, const QString &command, const QString &title) { if (m_connection->readyToStart()) { do { int job_id; /* Command file job */ char command_file[1024]; /* Command "file" */ http_status_t status; /* Document status */ cups_option_t hold_option; /* job-hold-until option */ /* * Create the CUPS command file... */ snprintf(command_file, sizeof(command_file), "#CUPS-COMMAND\n%s\n", command.toUtf8().constData()); /* * Send the command file job... */ hold_option.name = const_cast("job-hold-until"); hold_option.value = const_cast("no-hold"); if ((job_id = cupsCreateJob(CUPS_HTTP_DEFAULT, qUtf8Printable(printerName), qUtf8Printable(title), 1, &hold_option)) < 1) { qWarning() << "Unable to send command to printer driver!"; setError(HTTP_OK, IPP_NOT_POSSIBLE, i18n("Unable to send command to printer driver!")); setFinished(); return; } status = cupsStartDocument(CUPS_HTTP_DEFAULT, qUtf8Printable(printerName), job_id, nullptr, CUPS_FORMAT_COMMAND, 1); if (status == HTTP_CONTINUE) { status = cupsWriteRequestData(CUPS_HTTP_DEFAULT, command_file, strlen(command_file)); } if (status == HTTP_CONTINUE) { cupsFinishDocument(CUPS_HTTP_DEFAULT, qUtf8Printable(printerName)); } setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); if (httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError() >= IPP_REDIRECTION_OTHER_SITE) { qWarning() << "Unable to send command to printer driver!"; cupsCancelJob(qUtf8Printable(printerName), job_id); setFinished(); return; // Return to avoid a new try } } while (m_connection->retry("/", IPP_CREATE_JOB)); setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("printCommand", printerName, command, title); } } void KCupsRequest::cancelJob(const QString &printerName, int jobId) { KIppRequest request(IPP_CANCEL_JOB, QLatin1String("/jobs/")); request.addPrinterUri(printerName); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_JOB_ID, jobId); process(request); } void KCupsRequest::holdJob(const QString &printerName, int jobId) { KIppRequest request(IPP_HOLD_JOB, QLatin1String("/jobs/")); request.addPrinterUri(printerName); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_JOB_ID, jobId); process(request); } void KCupsRequest::releaseJob(const QString &printerName, int jobId) { KIppRequest request(IPP_RELEASE_JOB, QLatin1String("/jobs/")); request.addPrinterUri(printerName); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_JOB_ID, jobId); process(request); } void KCupsRequest::restartJob(const QString &printerName, int jobId) { KIppRequest request(IPP_RESTART_JOB, QLatin1String("/jobs/")); request.addPrinterUri(printerName); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_JOB_ID, jobId); process(request); } void KCupsRequest::moveJob(const QString &fromPrinterName, int jobId, const QString &toPrinterName) { if (jobId < -1 || fromPrinterName.isEmpty() || toPrinterName.isEmpty() || jobId == 0) { qWarning() << "Internal error, invalid input data" << jobId << fromPrinterName << toPrinterName; setFinished(); return; } KIppRequest request(CUPS_MOVE_JOB, QLatin1String("/jobs/")); request.addPrinterUri(fromPrinterName); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_JOB_ID, jobId); QString toPrinterUri = KIppRequest::assembleUrif(toPrinterName, false); request.addString(IPP_TAG_OPERATION, IPP_TAG_URI, KCUPS_JOB_PRINTER_URI, toPrinterUri); process(request); } +void KCupsRequest::authenticateJob(const QString &printerName, const QStringList authInfo, int jobId) +{ + KIppRequest request(IPP_OP_CUPS_AUTHENTICATE_JOB, QLatin1String("/jobs/")); + request.addPrinterUri(printerName); + request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_JOB_ID, jobId); + request.addStringList(IPP_TAG_OPERATION, IPP_TAG_TEXT, KCUPS_AUTH_INFO, authInfo); + + process(request); +} + void KCupsRequest::invokeMethod(const char *method, const QVariant &arg1, const QVariant &arg2, const QVariant &arg3, const QVariant &arg4, const QVariant &arg5, const QVariant &arg6, const QVariant &arg7, const QVariant &arg8) { m_error = IPP_OK; m_errorMsg.clear(); m_printers.clear(); m_jobs.clear(); m_ppds.clear(); m_ppdFile.clear(); // If this fails we get into a infinite loop // Do not use global()->thread() which point // to the KCupsConnection parent thread moveToThread(m_connection); m_finished = !QMetaObject::invokeMethod(this, method, Qt::QueuedConnection, QGenericArgument(arg1.typeName(), arg1.data()), QGenericArgument(arg2.typeName(), arg2.data()), QGenericArgument(arg3.typeName(), arg3.data()), QGenericArgument(arg4.typeName(), arg4.data()), QGenericArgument(arg5.typeName(), arg5.data()), QGenericArgument(arg6.typeName(), arg6.data()), QGenericArgument(arg7.typeName(), arg7.data()), QGenericArgument(arg8.typeName(), arg8.data())); if (m_finished) { setError(HTTP_ERROR, IPP_BAD_REQUEST, i18n("Failed to invoke method: %1", QLatin1String(method))); setFinished(); } } void KCupsRequest::process(const KIppRequest &request) { if (m_connection->readyToStart()) { m_connection->request(request); setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("process", QVariant::fromValue(request)); } } ReturnArguments KCupsRequest::ppds() const { return m_ppds; } KCupsServer KCupsRequest::serverSettings() const { return m_server; } QString KCupsRequest::printerPPD() const { return m_ppdFile; } KCupsPrinters KCupsRequest::printers() const { return m_printers; } KCupsJobs KCupsRequest::jobs() const { return m_jobs; } void KCupsRequest::waitTillFinished() { if (m_finished) { return; } m_loop.exec(); } bool KCupsRequest::hasError() const { return m_error; } ipp_status_t KCupsRequest::error() const { return m_error; } http_status_t KCupsRequest::httpStatus() const { return m_httpStatus; } QString KCupsRequest::errorMsg() const { return m_errorMsg; } KCupsConnection *KCupsRequest::connection() const { return m_connection; } void KCupsRequest::setError(http_status_t httpStatus, ipp_status_t error, const QString &errorMsg) { m_httpStatus = httpStatus; m_error = error; m_errorMsg = errorMsg; } void KCupsRequest::setFinished(bool delayed) { m_finished = true; if (delayed) { QTimer::singleShot(0, this, [this] () { emit finished(this); }); } else { emit finished(this); } } #include "moc_KCupsRequest.cpp" diff --git a/libkcups/KCupsRequest.h b/libkcups/KCupsRequest.h index ec99dd3..3393e27 100644 --- a/libkcups/KCupsRequest.h +++ b/libkcups/KCupsRequest.h @@ -1,323 +1,325 @@ /*************************************************************************** * Copyright (C) 2010-2012 by Daniel Nicoletti * * dantti12@gmail.com * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef KCUPS_REQUEST_H #define KCUPS_REQUEST_H #include #include #include "KCupsConnection.h" #include "KCupsJob.h" #include "KCupsPrinter.h" #include "KCupsServer.h" #include "KIppRequest.h" class Q_DECL_EXPORT KCupsRequest : public QObject { Q_OBJECT public: /** * Default constructor, it takes no parent * because it will move to KCupsConnection thread * * Before calling any method connect to finished() signal or * use waitTillFinished(). * You must delete the object manually after finished * using deleteLater(). */ explicit KCupsRequest(KCupsConnection *connection = nullptr); /** * This method creates an event loop * and quits after the request is finished */ void waitTillFinished(); /** * This method returns true if there was an error with the request */ bool hasError() const; ipp_status_t error() const; http_status_t httpStatus() const; QString serverError() const; QString errorMsg() const; KCupsConnection* connection() const; /** * Non empty when getPrinters is called and finish is emitted */ KCupsPrinters printers() const; /** * Non empty when getPPDs is called and finish is emitted */ ReturnArguments ppds() const; /** * Non empty when getServerSettings() is called and finish is emitted */ KCupsServer serverSettings() const; /** * Non empty when \sa getPrinterPPD() is called and finish is emitted * \warning You must unlik the given file name */ QString printerPPD() const; /** * Non empty when getJobs is called and finish is emitted */ KCupsJobs jobs() const; /** * Get all available PPDs from the givem make * @param make the maker of the printer */ Q_INVOKABLE void getPPDS(const QString &make = QString()); /** * Get all devices that could be added as a printer * This method emits device() */ Q_INVOKABLE void getDevices(int timeout = CUPS_TIMEOUT_DEFAULT); /** * Get all devices that could be added as a printer * This method emits device() */ Q_INVOKABLE void getDevices(int timeout, QStringList includeSchemes, QStringList excludeSchemes); /** * Get all available printers * @param mask filter the kind of printer that will be emitted (-1 to no filter) * @param requestedAttr the attributes to retrieve from cups * This method emits printer() * * THIS function can get the default server dest through the * "printer-is-default" attribute BUT it does not get user * defined default printer, see cupsGetDefault() on www.cups.org for details */ Q_INVOKABLE void getPrinters(QStringList attributes, int mask = -1); /** * Get attributes from a given printer * @param printer The printer to apply the change * @param isClass True it is a printer class * @param attributes The attributes you are requesting * * @return The return will be stored in \sa printers() */ Q_INVOKABLE void getPrinterAttributes(const QString &printerName, bool isClass, QStringList attributes); /** * Get all jobs * This method emits job() * TODO we need to see if we authenticate as root to do some taks * the myJobs will return the user's jobs or the root's jobs * @param printer which printer you are requiring jobs for (empty = all printers) * @param myJobs true if you only want your jobs * @param whichJobs which kind jobs should be sent */ Q_INVOKABLE void getJobs(const QString &printerName, bool myJobs, int whichJobs, QStringList attributes); /** * Get attributes from a given printer * @param printer The printer to apply the change * @param isClass True it is a printer class * @param attributes The attributes you are requesting * * @return The return will be stored in \sa printers() */ Q_INVOKABLE void getJobAttributes(int jobId, const QString &printerUri, QStringList attributes); /** * Get the CUPS server settings * This method emits server() */ Q_INVOKABLE void getServerSettings(); /** * Get the PPD associated with @arg printerName * the result is stored at \sa printerPPD() */ Q_INVOKABLE void getPrinterPPD(const QString &printerName); /** * Get the CUPS server settings * @param userValues the new server settings */ Q_INVOKABLE void setServerSettings(const KCupsServer &server); // ---- Printer Methods /** * Add or Modify a Printer * @param printerName The printer to apply the change * @param attributes The new attributes of the printer * @param filename The file name in case of changing the PPD */ void addOrModifyPrinter(const QString &printerName, const QVariantHash &attributes, const QString &filename = QString()); /** * Add or Modify a Class * @param className The class to apply the change * @param attributes The new attributes of the printer */ void addOrModifyClass(const QString &className, const QVariantHash &attributes); /** * Set if a given printer should be shared among other cups * @param printer The printer to apply the change * @param isClass True it is a printer class * @param shared True if it should be shared */ void setShared(const QString &printerName, bool isClass, bool shared); /** * Set if a given printer should be the default one among others * @param printer The printer to apply the change */ void setDefaultPrinter(const QString &printerName); /** * Pause the given printer from receiving jobs * @param printer The printer to apply the change */ void pausePrinter(const QString &printerName); /** * Resume the given printer from receiving jobs * @param printer The printer to apply the change */ void resumePrinter(const QString &printerName); /** * Allows the given printer from receiving jobs * @param printer The printer to apply the change */ void acceptJobs(const QString &printerName); /** * Prevents the given printer from receiving jobs * @param printer The printer to apply the change */ void rejectJobs(const QString &printerName); /** * Delete the given printer, if it's not local it's not * possible to delete it * @param printer The printer to apply the change */ void deletePrinter(const QString &printerName); /** * Print a test page * @param printerName The printer where the test should be done * @param isClass True it is a printer class */ void printTestPage(const QString &printerName, bool isClass); /** * Print a command test * @param printerName The printer where the test should be done * @param command The command to print * @param title The title of the command */ Q_INVOKABLE void printCommand(const QString &printerName, const QString &command, const QString &title); // Jobs methods /** * Cancels tries to cancel a given job * @param printerName the destination name (printer) * @param jobId the job identification */ void cancelJob(const QString &printerName, int jobId); /** * Holds the printing of a given job * @param printerName the destination name (printer) * @param jobId the job identification */ void holdJob(const QString &printerName, int jobId); /** * Holds the printing of a given job * @param printerName the destination name (printer) * @param jobId the job identification */ void releaseJob(const QString &printerName, int jobId); /** * Restart the printing of a given job * @param printerName the destination name (printer) * @param jobId the job identification */ void restartJob(const QString &printerName, int jobId); /** * Holds the printing of a given job * @param fromDestName the destination name which holds the job * @param jobId the job identification * @param toDestName the destination to hold the job */ void moveJob(const QString &fromPrinterName, int jobId, const QString &toPrinterName); + void authenticateJob(const QString &printerName, const QStringList authInfo, int jobId); + signals: void device(const QString &device_class, const QString &device_id, const QString &device_info, const QString &device_make_and_model, const QString &device_uri, const QString &device_location); void finished(KCupsRequest *); private: void invokeMethod(const char *method, const QVariant &arg1 = QVariant(), const QVariant &arg2 = QVariant(), const QVariant &arg3 = QVariant(), const QVariant &arg4 = QVariant(), const QVariant &arg5 = QVariant(), const QVariant &arg6 = QVariant(), const QVariant &arg7 = QVariant(), const QVariant &arg8 = QVariant()); Q_INVOKABLE void process(const KIppRequest &request); void setError(http_status_t httpStatus, ipp_status_t error, const QString &errorMsg); void setFinished(bool delayed = false); KCupsConnection *m_connection; QEventLoop m_loop; bool m_finished = true; ipp_status_t m_error = IPP_OK; http_status_t m_httpStatus; QString m_errorMsg; ReturnArguments m_ppds; KCupsServer m_server; QString m_ppdFile; KCupsPrinters m_printers; KCupsJobs m_jobs; }; #endif // KCUPS_REQUEST_H diff --git a/printqueue/PrintQueueUi.cpp b/printqueue/PrintQueueUi.cpp index aad092d..31854c4 100644 --- a/printqueue/PrintQueueUi.cpp +++ b/printqueue/PrintQueueUi.cpp @@ -1,613 +1,707 @@ /*************************************************************************** * Copyright (C) 2010-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "PrintQueueUi.h" #include "ui_PrintQueueUi.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include +#include +#include #define PRINTER_ICON_SIZE 92 PrintQueueUi::PrintQueueUi(const KCupsPrinter &printer, QWidget *parent) : QDialog(parent), ui(new Ui::PrintQueueUi), m_destName(printer.name()) { ui->setupUi(this); // since setupUi needs to setup on the mainWidget() // we need to manually connect the buttons connect(ui->cancelJobPB, &QPushButton::clicked, this, &PrintQueueUi::cancelJob); connect(ui->holdJobPB, &QPushButton::clicked, this, &PrintQueueUi::holdJob); connect(ui->resumeJobPB, &QPushButton::clicked, this, &PrintQueueUi::resumeJob); connect(ui->reprintPB, &QPushButton::clicked, this, &PrintQueueUi::reprintJob); connect(ui->pausePrinterPB, &QPushButton::clicked, this, &PrintQueueUi::pausePrinter); connect(ui->configurePrinterPB, &QPushButton::clicked, this, &PrintQueueUi::configurePrinter); connect(ui->whichJobsCB, static_cast(&QComboBox::currentIndexChanged), this, &PrintQueueUi::whichJobsIndexChanged); // Needed so we have our dialog size saved setAttribute(Qt::WA_DeleteOnClose); setWindowIcon(printer.icon()); if (printer.info().isEmpty()) { m_title = printer.name(); } else { m_title = printer.name() % QLatin1String(" - ") % printer.info(); } setWindowTitle(m_title); setSizeGripEnabled(true); (void) minimumSizeHint(); //Force the dialog to be laid out now layout()->setContentsMargins(0,0,0,0); m_isClass = printer.isClass(); // setup default options ui->jobsView->setCornerWidget(new QWidget); setupButtons(); // loads the standard key icon m_printerIcon = printer.icon().pixmap(PRINTER_ICON_SIZE, PRINTER_ICON_SIZE); ui->iconL->setPixmap(m_printerIcon); m_pauseIcon = KIconLoader::global()->loadIcon(QLatin1String("media-playback-pause"), KIconLoader::NoGroup, KIconLoader::SizeMedium, KIconLoader::DefaultState, QStringList(), nullptr, true); ui->printerStatusMsgL->setText(QString()); // setup the jobs model m_model = new JobModel(this); m_model->setParentWId(winId()); m_model->init(printer.name()); connect(m_model, &JobModel::dataChanged, this, &PrintQueueUi::updateButtons); connect(m_model, &JobModel::dataChanged, this, &PrintQueueUi::update); m_proxyModel = new JobSortFilterModel(this); m_proxyModel->setSourceModel(m_model); m_proxyModel->setDynamicSortFilter(true); ui->jobsView->setModel(m_proxyModel); ui->jobsView->setItemDelegate(new NoSelectionRectDelegate(this)); // sort by status column means the jobs will be sorted by the queue order ui->jobsView->sortByColumn(JobModel::ColStatus, Qt::AscendingOrder); connect(ui->jobsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &PrintQueueUi::updateButtons); connect(ui->jobsView, &QTreeView::customContextMenuRequested, this, &PrintQueueUi::showContextMenu); ui->jobsView->header()->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->jobsView->header(), &QHeaderView::customContextMenuRequested, this, &PrintQueueUi::showHeaderContextMenu); QHeaderView *header = ui->jobsView->header(); header->setSectionResizeMode(QHeaderView::Interactive); header->setStretchLastSection(false); header->setSectionResizeMode(JobModel::ColStatus, QHeaderView::ResizeToContents); header->setSectionResizeMode(JobModel::ColName, QHeaderView::Stretch); header->setSectionResizeMode(JobModel::ColUser, QHeaderView::ResizeToContents); header->setSectionResizeMode(JobModel::ColCreated, QHeaderView::ResizeToContents); header->setSectionResizeMode(JobModel::ColCompleted, QHeaderView::ResizeToContents); header->setSectionResizeMode(JobModel::ColPages, QHeaderView::ResizeToContents); header->setSectionResizeMode(JobModel::ColProcessed, QHeaderView::ResizeToContents); header->setSectionResizeMode(JobModel::ColSize, QHeaderView::ResizeToContents); header->setSectionResizeMode(JobModel::ColStatusMessage, QHeaderView::ResizeToContents); header->setSectionResizeMode(JobModel::ColPrinter, QHeaderView::ResizeToContents); KConfigGroup printQueue(KSharedConfig::openConfig(QLatin1String("print-manager")), "PrintQueue"); if (printQueue.hasKey("ColumnState")) { // restore the header state order header->restoreState(printQueue.readEntry("ColumnState", QByteArray())); } else { // Hide some columns ColPrinter header->hideSection(JobModel::ColPrinter); header->hideSection(JobModel::ColUser); header->hideSection(JobModel::ColCompleted); header->hideSection(JobModel::ColSize); header->hideSection(JobModel::ColFromHost); } // This is emitted when a printer is modified connect(KCupsConnection::global(), &KCupsConnection::printerModified, this, &PrintQueueUi::updatePrinter); // This is emitted when a printer has it's state changed connect(KCupsConnection::global(), &KCupsConnection::printerStateChanged, this, &PrintQueueUi::updatePrinter); // This is emitted when a printer is stopped connect(KCupsConnection::global(), &KCupsConnection::printerStopped, this, &PrintQueueUi::updatePrinter); // This is emitted when a printer is restarted connect(KCupsConnection::global(), &KCupsConnection::printerRestarted, this, &PrintQueueUi::updatePrinter); // This is emitted when a printer is shutdown connect(KCupsConnection::global(), &KCupsConnection::printerShutdown, this, &PrintQueueUi::updatePrinter); // This is emitted when a printer is removed connect(KCupsConnection::global(), &KCupsConnection::printerDeleted, this, &PrintQueueUi::updatePrinter); // This is emitted when a printer/queue is changed // Deprecated stuff that works better than the above connect(KCupsConnection::global(), &KCupsConnection::rhPrinterAdded, this, &PrintQueueUi::updatePrinterByName); connect(KCupsConnection::global(), &KCupsConnection::rhPrinterRemoved, this, &PrintQueueUi::updatePrinterByName); connect(KCupsConnection::global(), &KCupsConnection::rhQueueChanged, this, &PrintQueueUi::updatePrinterByName); updatePrinterByName(m_destName); // Restore the dialog size KConfigGroup configGroup(KSharedConfig::openConfig(QLatin1String("print-manager")), "PrintQueue"); KWindowConfig::restoreWindowSize(windowHandle(), configGroup); auto delJobShortcut = new QShortcut(QKeySequence::Delete, ui->jobsView); delJobShortcut->setContext(Qt::WidgetShortcut); connect(delJobShortcut, &QShortcut::activated, this, &PrintQueueUi::cancelJob); } PrintQueueUi::~PrintQueueUi() { KConfigGroup configGroup(KSharedConfig::openConfig(QLatin1String("print-manager")), "PrintQueue"); // save the header state order configGroup.writeEntry("ColumnState", ui->jobsView->header()->saveState()); // Save the dialog size KWindowConfig::saveWindowSize(windowHandle(), configGroup); delete ui; } int PrintQueueUi::columnCount(const QModelIndex &parent) const { if (!parent.isValid()) { return JobModel::LastColumn; } return 0; } void PrintQueueUi::setState(int state, const QString &message) { qDebug() << state << message; if (state != m_lastState || ui->printerStatusMsgL->text() != message) { // save the last state so the ui doesn't need to keep updating if (ui->printerStatusMsgL->text() != message) { ui->printerStatusMsgL->setText(message); } m_lastState = state; QPixmap icon(m_printerIcon); m_printerPaused = false; switch (state) { case KCupsPrinter::Idle: ui->statusL->setText(i18n("Printer ready")); ui->pausePrinterPB->setText(i18n("Pause Printer")); ui->pausePrinterPB->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); break; case KCupsPrinter::Printing: if (!m_title.isNull()) { QString jobTitle = m_model->processingJob(); if (jobTitle.isEmpty()) { ui->statusL->setText(i18n("Printing...")); } else { ui->statusL->setText(i18n("Printing '%1'", jobTitle)); } ui->pausePrinterPB->setText(i18n("Pause Printer")); ui->pausePrinterPB->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); } break; case KCupsPrinter::Stopped: m_printerPaused = true; ui->statusL->setText(i18n("Printer paused")); ui->pausePrinterPB->setText(i18n("Resume Printer")); ui->pausePrinterPB->setIcon(QIcon::fromTheme(QLatin1String("media-playback-start"))); // create a painter to paint the action icon over the key icon { QPainter painter(&icon); // the emblem icon to size 32 int overlaySize = KIconLoader::SizeMedium; QPoint startPoint; // bottom right corner startPoint = QPoint(PRINTER_ICON_SIZE - overlaySize - 2, PRINTER_ICON_SIZE - overlaySize - 2); painter.drawPixmap(startPoint, m_pauseIcon); } break; default : ui->statusL->setText(i18n("Printer state unknown")); break; } // set the printer icon setWindowIcon(icon); } } void PrintQueueUi::showContextMenu(const QPoint &point) { // check if the click was actually over a job if (!ui->jobsView->indexAt(point).isValid() || m_preparingMenu) { return; } - m_preparingMenu = true; - bool moveTo = false; - QItemSelection selection; // we need to map the selection to source to get the real indexes - selection = m_proxyModel->mapSelectionToSource(ui->jobsView->selectionModel()->selection()); + QItemSelection selection = m_proxyModel->mapSelectionToSource(ui->jobsView->selectionModel()->selection()); // if the selection is empty the user clicked on an empty space - if (!selection.indexes().isEmpty()) { - const QModelIndexList indexes = selection.indexes(); - for (const QModelIndex &index : indexes) { - if (index.column() == 0 && index.flags() & Qt::ItemIsDragEnabled) { + if (selection.indexes().isEmpty()) { + return; + } + + m_preparingMenu = true; + + // context menu + auto menu = new QMenu(this); + menu->setAttribute(Qt::WA_DeleteOnClose); + + bool moveTo = false; + bool authenticate = false; + const QModelIndexList indexes = selection.indexes(); + for (const QModelIndex &index : indexes) { + if (index.column() == JobModel::Columns::ColStatus) { + if (index.flags() & Qt::ItemIsDragEnabled) { // Found a move to item moveTo = true; - break; } - } - // if we can move a job create the menu - if (moveTo) { - // context menu - auto menu = new QMenu(this); - // move to menu - auto moveToMenu = new QMenu(i18n("Move to"), this); - - // get printers we can move to - QPointer request = new KCupsRequest; - request->getPrinters({ KCUPS_PRINTER_NAME, KCUPS_PRINTER_INFO }); - request->waitTillFinished(); - if (!request) { - return; + if (index.data(JobModel::Role::RoleJobAuthenticationRequired).toBool()) { + // Found an item which requires authentication + authenticate = true; } - const KCupsPrinters printers = request->printers(); - request->deleteLater(); + } + } - for (const KCupsPrinter &printer : printers) { - // If there is a printer and it's not the current one add it - // as a new destination - if (printer.name() != m_destName) { - QAction *action = moveToMenu->addAction(printer.info()); - action->setData(printer.name()); - } - } + // if we can move a job create the menu + if (moveTo) { + // move to menu + auto moveToMenu = new QMenu(i18n("Move to"), menu); + + // get printers we can move to + QPointer request = new KCupsRequest; + request->getPrinters({ KCUPS_PRINTER_NAME, KCUPS_PRINTER_INFO }); + request->waitTillFinished(); + if (!request) { + return; + } + const KCupsPrinters printers = request->printers(); + request->deleteLater(); - if (!moveToMenu->isEmpty()) { - menu->addMenu(moveToMenu); - // show the menu on the right point - QAction *action = menu->exec(ui->jobsView->mapToGlobal(point)); - if (action) { - // move the job - modifyJob(JobModel::Move, action->data().toString()); - } + for (const KCupsPrinter &printer : printers) { + // If there is a printer and it's not the current one add it + // as a new destination + if (printer.name() != m_destName) { + QAction *action = moveToMenu->addAction(printer.info()); + action->setData(printer.name()); + connect(action, &QAction::triggered, this, [=] () { + this->modifyJob(JobModel::Move, action->data().toString()); + }); } } + + if (!moveToMenu->isEmpty()) { + menu->addMenu(moveToMenu); + } } + + // Authenticate + if (authenticate) { + QAction *action = menu->addAction(i18n("Authenticate")); + connect(action, &QAction::triggered, this, &PrintQueueUi::authenticateJob); + } + + if (menu->isEmpty()) { + delete menu; + } else { + // show the menu on the right point + menu->popup(ui->jobsView->mapToGlobal(point)); + } + m_preparingMenu = false; } void PrintQueueUi::showHeaderContextMenu(const QPoint &point) { // Displays a menu containing the header name, and // a check box to indicate if it's being shown auto menu = new QMenu(this); for (int i = 0; i < m_proxyModel->columnCount(); i++) { auto name = m_proxyModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(); QAction *action = menu->addAction(name); action->setCheckable(true); action->setChecked(!ui->jobsView->header()->isSectionHidden(i)); action->setData(i); } QAction *action = menu->exec(ui->jobsView->header()->mapToGlobal(point)); if (action) { int section = action->data().toInt(); if (action->isChecked()) { ui->jobsView->header()->showSection(section); } else { ui->jobsView->header()->hideSection(section); } } } void PrintQueueUi::updatePrinterByName(const QString &printer) { qDebug() << printer << m_destName; if (printer != m_destName) { // It was another printer that changed return; } const QStringList attr({ KCUPS_PRINTER_INFO, KCUPS_PRINTER_TYPE, KCUPS_PRINTER_STATE, KCUPS_PRINTER_STATE_MESSAGE, }); auto request = new KCupsRequest; connect(request, &KCupsRequest::finished, this, &PrintQueueUi::getAttributesFinished); request->getPrinterAttributes(printer, m_isClass, attr); } void PrintQueueUi::updatePrinter(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs) { // REALLY? all these parameters just to say foo was added?? Q_UNUSED(text) Q_UNUSED(printerUri) Q_UNUSED(printerState) Q_UNUSED(printerStateReasons) Q_UNUSED(printerIsAcceptingJobs) qDebug() << printerName << printerStateReasons; updatePrinterByName(printerName); } void PrintQueueUi::getAttributesFinished(KCupsRequest *request) { qDebug() << request->hasError() << request->printers().isEmpty(); if (request->hasError() || request->printers().isEmpty()) { // if cups stops we disable our queue setEnabled(false); request->deleteLater(); // DO not delete before using as the request is in another thread return; } else if (isEnabled() == false) { // if cups starts again we enable our queue setEnabled(true); } KCupsPrinter printer = request->printers().first(); // get printer-info if (printer.info().isEmpty()) { m_title = printer.name(); } else { m_title = printer.name() + QLatin1String(" - ") + printer.info(); } // get printer-state setState(printer.state(), printer.stateMsg()); // store if the printer is a class m_isClass = printer.isClass(); request->deleteLater(); update(); } void PrintQueueUi::update() { // Set window title if (m_model->rowCount()) { if (m_destName.isNull()) { setWindowTitle(i18np("All Printers (%1 Job)", "All Printers (%1 Jobs)", m_model->rowCount())); } else { setWindowTitle(i18np("%2 (%1 Job)", "%2 (%1 Jobs)", m_model->rowCount(), m_title)); } } else { setWindowTitle(m_destName.isNull() ? i18n("All Printers") : m_title); } } void PrintQueueUi::updateButtons() { bool cancel, hold, release, reprint; // Set all options to false cancel = hold = release = reprint = false; QItemSelection selection; // we need to map the selection to source to get the real indexes selection = m_proxyModel->mapSelectionToSource(ui->jobsView->selectionModel()->selection()); // enable or disable the job action buttons if something is selected if (!selection.indexes().isEmpty()) { const QModelIndexList indexes = selection.indexes(); for (const QModelIndex &index : indexes) { if (index.column() == 0) { switch (static_cast(index.data(JobModel::RoleJobState).toInt())) { case IPP_JOB_CANCELED : case IPP_JOB_COMPLETED : case IPP_JOB_ABORTED : break; case IPP_JOB_HELD : case IPP_JOB_STOPPED : release = true; cancel = true; break; default: cancel = hold = true; break; } if (index.data(JobModel::RoleJobRestartEnabled).toBool()) { reprint = true; } } } } ui->cancelJobPB->setEnabled(cancel); ui->holdJobPB->setEnabled(hold); ui->resumeJobPB->setEnabled(release); ui->reprintPB->setEnabled(reprint); } void PrintQueueUi::modifyJob(int action, const QString &destName) { // get all selected indexes QItemSelection selection; // we need to map the selection to source to get the real indexes selection = m_proxyModel->mapSelectionToSource(ui->jobsView->selectionModel()->selection()); const QModelIndexList indexes = selection.indexes(); for (const QModelIndex &index : indexes) { if (index.column() == 0) { KCupsRequest *request; request = m_model->modifyJob(index.row(), static_cast(action), destName); if (!request) { // probably the job already has this state // or this is an unknown action continue; } request->waitTillFinished(); if (request->hasError()) { QString msg, jobName; jobName = m_model->item(index.row(), static_cast(JobModel::ColName))->text(); switch (action) { case JobModel::Cancel: msg = i18n("Failed to cancel '%1'", jobName); break; case JobModel::Hold: msg = i18n("Failed to hold '%1'", jobName); break; case JobModel::Release: msg = i18n("Failed to release '%1'", jobName); break; case JobModel::Reprint: msg = i18n("Failed to reprint '%1'", jobName); break; case JobModel::Move: msg = i18n("Failed to move '%1' to '%2'", jobName, destName); break; } KMessageBox::detailedSorry(this, msg, request->errorMsg(), i18n("Failed")); } request->deleteLater(); } } } void PrintQueueUi::pausePrinter() { // STOP and RESUME printer QPointer request = new KCupsRequest; if (m_printerPaused) { qDebug() << m_destName << "m_printerPaused"; request->resumePrinter(m_destName); } else { qDebug() << m_destName << "NOT m_printerPaused"; request->pausePrinter(m_destName); } request->waitTillFinished(); if (request) { request->deleteLater(); } } void PrintQueueUi::configurePrinter() { QProcess::startDetached(QLatin1String("configure-printer"), {m_destName}); } void PrintQueueUi::cancelJob() { // CANCEL a job modifyJob(JobModel::Cancel); } void PrintQueueUi::holdJob() { // HOLD a job modifyJob(JobModel::Hold); } void PrintQueueUi::resumeJob() { // RESUME a job modifyJob(JobModel::Release); } void PrintQueueUi::reprintJob() { modifyJob(JobModel::Reprint); } +void PrintQueueUi::authenticateJob() +{ + QScopedPointer request(new KCupsRequest); + request->getPrinterAttributes(m_destName, m_isClass, { KCUPS_PRINTER_URI_SUPPORTED, KCUPS_AUTH_INFO_REQUIRED }); + request->waitTillFinished(); + if (request->hasError() || request->printers().size() != 1) { + qWarning() << "Ignoring request, printer not found or error" << m_destName << request->errorMsg(); + } + + const KCupsPrinter printer = request->printers().first(); + + KIO::AuthInfo info; + info.keepPassword = true; + info.prompt = i18n("Enter credentials to print from %1", m_destName); + info.url = QUrl(printer.uriSupported()); + if (printer.authInfoRequired().contains(QStringLiteral("domain"))) { + info.setExtraField(QStringLiteral("domain"), QStringLiteral("")); + } + + QScopedPointer passwdServerClient(new KPasswdServerClient()); + + auto winId = static_cast(this->winId()); + auto usertime = static_cast(KUserTimestamp::userTimestamp()); + if (passwdServerClient->checkAuthInfo(&info, winId, usertime)) { + // at this stage we don't know if stored credentials are correct + // trying blindly is risky, it may block this user's account + passwdServerClient->removeAuthInfo(info.url.host(), info.url.scheme(), info.username); + } + const int passwordDialogErrorCode = passwdServerClient->queryAuthInfo(&info, QString(), winId, usertime); + if (passwordDialogErrorCode != KJob::NoError) { + // user cancelled or kiod_kpasswdserver not running + qDebug() << "queryAuthInfo error code" << passwordDialogErrorCode; + return; + } + + QStringList authInfo; + for (QString &authInfoRequired : printer.authInfoRequired()) { + if (authInfoRequired == QStringLiteral("domain")) { + authInfo << info.getExtraField(QStringLiteral("domain")).toString(); + } else if (authInfoRequired == QStringLiteral("username")) { + authInfo << info.username; + } else if (authInfoRequired == QStringLiteral("password")) { + authInfo << info.password; + } else { + qWarning() << "No auth info for: " << authInfoRequired; + authInfo << QString(); + } + } + + // we need to map the selection to source to get the real indexes + QItemSelection selection = m_proxyModel->mapSelectionToSource(ui->jobsView->selectionModel()->selection()); + const QModelIndexList indexes = selection.indexes(); + for (const QModelIndex &index : indexes) { + if (index.column() == JobModel::Columns::ColStatus) { + QScopedPointer authRequest(new KCupsRequest); + authRequest->authenticateJob(m_destName, authInfo, index.data(JobModel::Role::RoleJobId).toInt()); + authRequest->waitTillFinished(); + if (authRequest->hasError()) { + qWarning() << "Error authenticating jobs" << authRequest->error() << authRequest->errorMsg(); + // remove cache on fail + passwdServerClient->removeAuthInfo(info.url.host(), info.url.scheme(), info.username); + return; + } + } + } + +} + void PrintQueueUi::whichJobsIndexChanged(int index) { switch (index) { case 1: m_model->setWhichJobs(JobModel::WhichCompleted); break; case 2: m_model->setWhichJobs(JobModel::WhichAll); break; default: m_model->setWhichJobs(JobModel::WhichActive); break; } } void PrintQueueUi::setupButtons() { // setup jobs buttons // cancel action ui->cancelJobPB->setIcon(QIcon::fromTheme(QLatin1String("dialog-cancel"))); // hold job action ui->holdJobPB->setIcon(QIcon::fromTheme(QLatin1String("document-open-recent"))); // resume job action // TODO we need a new icon ui->resumeJobPB->setIcon(QIcon::fromTheme(QLatin1String("media-playback-start"))); ui->reprintPB->setIcon(QIcon::fromTheme(QLatin1String("view-refresh"))); const QIcon viewFilterIcon = QIcon::fromTheme(QLatin1String("view-filter")); ui->whichJobsCB->setItemIcon(0, viewFilterIcon); ui->whichJobsCB->setItemIcon(1, viewFilterIcon); ui->whichJobsCB->setItemIcon(2, viewFilterIcon); // stop start printer ui->pausePrinterPB->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); // configure printer ui->configurePrinterPB->setIcon(QIcon::fromTheme(QLatin1String("configure"))); } void PrintQueueUi::closeEvent(QCloseEvent *event) { // emits finished signal to be removed the cache emit finished(); QWidget::closeEvent(event); } #include "moc_PrintQueueUi.cpp" diff --git a/printqueue/PrintQueueUi.h b/printqueue/PrintQueueUi.h index e594a4e..5518f2d 100644 --- a/printqueue/PrintQueueUi.h +++ b/printqueue/PrintQueueUi.h @@ -1,94 +1,95 @@ /*************************************************************************** * Copyright (C) 2010-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef PRINT_QUEUE_UI_H #define PRINT_QUEUE_UI_H #include #include #include namespace Ui { class PrintQueueUi; } class KCupsRequest; class KCupsPrinter; class JobSortFilterModel; class JobModel; class PrintQueueUi : public QDialog { Q_OBJECT public: explicit PrintQueueUi(const KCupsPrinter &printer, QWidget *parent = nullptr); ~PrintQueueUi() override; signals: void finished(); public slots: void update(); private slots: void updatePrinterByName(const QString &printer); void updatePrinter(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); void whichJobsIndexChanged(int index); void pausePrinter(); void configurePrinter(); void cancelJob(); void holdJob(); void resumeJob(); void reprintJob(); + void authenticateJob(); int columnCount(const QModelIndex &parent = QModelIndex()) const; void updateButtons(); void showContextMenu(const QPoint &point); void showHeaderContextMenu(const QPoint &point); void getAttributesFinished(KCupsRequest *request); private: void closeEvent(QCloseEvent *event) override; void setupButtons(); void setState(int state, const QString &message); void modifyJob(int action, const QString &destName = QString()); Ui::PrintQueueUi *ui; QToolButton *m_filterJobs; JobSortFilterModel *m_proxyModel; JobModel *m_model; QString m_destName; QString m_title; QPixmap m_printerIcon; QPixmap m_pauseIcon; QPixmap m_startIcon; QPixmap m_warningIcon; char m_lastState = 0; bool m_isClass; bool m_preparingMenu = false; bool m_printerPaused = false; }; #endif