mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-24 19:02:53 +00:00
878 lines
32 KiB
C++
878 lines
32 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2010-2012 by Daniel Nicoletti *
|
|
* dantti12@gmail.com *
|
|
* Copyright (C) 2012 Harald Sitter <sitter@kde.org> *
|
|
* *
|
|
* 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 "KCupsConnection.h"
|
|
|
|
#include "KCupsPasswordDialog.h"
|
|
#include "KIppRequest.h"
|
|
|
|
#include <config.h>
|
|
|
|
#include <QCoreApplication>
|
|
#include <QStringBuilder>
|
|
#include <QDBusConnection>
|
|
#include <QByteArray>
|
|
|
|
#include <KLocale>
|
|
#include <KDebug>
|
|
|
|
#include <cups/cups.h>
|
|
|
|
#define RENEW_INTERVAL 3500
|
|
#define SUBSCRIPTION_DURATION 3600
|
|
|
|
#define DBUS_SERVER_RESTARTED "server-restarted" // ServerRestarted
|
|
#define DBUS_SERVER_STARTED "server-started" // ServerStarted
|
|
#define DBUS_SERVER_STOPPED "server-stopped" // ServerStopped
|
|
#define DBUS_SERVER_AUDIT "server-audit" // ServerAudit
|
|
|
|
#define DBUS_PRINTER_RESTARTED "printer-restarted" // PrinterRestarted
|
|
#define DBUS_PRINTER_SHUTDOWN "printer-shutdown" // PrinterShutdown
|
|
#define DBUS_PRINTER_STOPPED "printer-stopped" // PrinterStopped
|
|
#define DBUS_PRINTER_STATE_CHANGED "printer-state-changed" // PrinterStateChanged
|
|
#define DBUS_PRINTER_FINISHINGS_CHANGED "printer-finishings-changed" // PrinterFinishingsChanged
|
|
#define DBUS_PRINTER_MEDIA_CHANGED "printer-media-changed" // PrinterMediaChanged
|
|
#define DBUS_PRINTER_ADDED "printer-added" // PrinterAdded
|
|
#define DBUS_PRINTER_DELETED "printer-deleted" // PrinterDeleted
|
|
#define DBUS_PRINTER_MODIFIED "printer-modified" // PrinterModified
|
|
|
|
#define DBUS_JOB_STATE_CHANGED "job-state-changed" // JobState
|
|
#define DBUS_JOB_CREATED "job-created" // JobCreated
|
|
#define DBUS_JOB_COMPLETED "job-completed" // JobCompleted
|
|
#define DBUS_JOB_STOPPED "job-stopped" // JobStopped
|
|
#define DBUS_JOB_CONFIG_CHANGED "job-config-changed" // JobConfigChanged
|
|
#define DBUS_JOB_PROGRESS "job-progress" // JobProgress
|
|
|
|
Q_DECLARE_METATYPE(QList<int>)
|
|
Q_DECLARE_METATYPE(QList<bool>)
|
|
|
|
KCupsConnection* KCupsConnection::m_instance = 0;
|
|
static int password_retries = 0;
|
|
static int internalErrorCount = 0;
|
|
const char * password_cb(const char *prompt, http_t *http, const char *method, const char *resource, void *user_data);
|
|
|
|
KCupsConnection* KCupsConnection::global()
|
|
{
|
|
if (!m_instance) {
|
|
m_instance = new KCupsConnection(qApp);
|
|
}
|
|
|
|
return m_instance;
|
|
}
|
|
|
|
KCupsConnection::KCupsConnection(QObject *parent) :
|
|
QThread(parent)
|
|
{
|
|
init();
|
|
}
|
|
|
|
KCupsConnection::KCupsConnection(const KUrl &server, QObject *parent) :
|
|
QThread(parent),
|
|
m_serverUrl(server)
|
|
{
|
|
qRegisterMetaType<KIppRequest>("KIppRequest");
|
|
init();
|
|
}
|
|
|
|
KCupsConnection::~KCupsConnection()
|
|
{
|
|
if (m_instance == this) {
|
|
m_instance = 0;
|
|
}
|
|
m_passwordDialog->deleteLater();
|
|
|
|
quit();
|
|
wait();
|
|
|
|
delete m_renewTimer;
|
|
delete m_subscriptionTimer;
|
|
}
|
|
|
|
void KCupsConnection::setPasswordMainWindow(WId mainwindow)
|
|
{
|
|
m_passwordDialog->setMainWindow(mainwindow);
|
|
}
|
|
|
|
void KCupsConnection::init()
|
|
{
|
|
// Creating the dialog before start() will make it run on the gui thread
|
|
m_passwordDialog = new KCupsPasswordDialog;
|
|
m_subscriptionId = -1;
|
|
m_inited = false;
|
|
|
|
// setup the DBus subscriptions
|
|
|
|
// Server related signals
|
|
// ServerStarted
|
|
notifierConnect(QLatin1String("ServerStarted"),
|
|
this,
|
|
SIGNAL(serverStarted(QString)));
|
|
|
|
// ServerStopped
|
|
notifierConnect(QLatin1String("ServerStopped"),
|
|
this,
|
|
SIGNAL(serverStopped(QString)));
|
|
|
|
// ServerRestarted
|
|
notifierConnect(QLatin1String("ServerRestarted"),
|
|
this,
|
|
SIGNAL(serverRestarted(QString)));
|
|
|
|
// ServerAudit
|
|
notifierConnect(QLatin1String("ServerAudit"),
|
|
this,
|
|
SIGNAL(serverAudit(QString)));
|
|
|
|
// Printer related signals
|
|
// PrinterAdded
|
|
notifierConnect(QLatin1String("PrinterAdded"),
|
|
this,
|
|
SIGNAL(printerAdded(QString,QString,QString,uint,QString,bool)));
|
|
|
|
// PrinterModified
|
|
notifierConnect(QLatin1String("PrinterModified"),
|
|
this,
|
|
SIGNAL(printerModified(QString,QString,QString,uint,QString,bool)));
|
|
|
|
// PrinterDeleted
|
|
notifierConnect(QLatin1String("PrinterDeleted"),
|
|
this,
|
|
SIGNAL(printerDeleted(QString,QString,QString,uint,QString,bool)));
|
|
|
|
// PrinterStateChanged
|
|
notifierConnect(QLatin1String("PrinterStateChanged"),
|
|
this,
|
|
SIGNAL(printerStateChanged(QString,QString,QString,uint,QString,bool)));
|
|
|
|
// PrinterStopped
|
|
notifierConnect(QLatin1String("PrinterStopped"),
|
|
this,
|
|
SIGNAL(printerStopped(QString,QString,QString,uint,QString,bool)));
|
|
|
|
// PrinterShutdown
|
|
notifierConnect(QLatin1String("PrinterShutdown"),
|
|
this,
|
|
SIGNAL(printerShutdown(QString,QString,QString,uint,QString,bool)));
|
|
|
|
// PrinterRestarted
|
|
notifierConnect(QLatin1String("PrinterRestarted"),
|
|
this,
|
|
SIGNAL(printerRestarted(QString,QString,QString,uint,QString,bool)));
|
|
|
|
// PrinterMediaChanged
|
|
notifierConnect(QLatin1String("PrinterMediaChanged"),
|
|
this,
|
|
SIGNAL(printerMediaChanged(QString,QString,QString,uint,QString,bool)));
|
|
|
|
// PrinterFinishingsChanged
|
|
notifierConnect(QLatin1String("PrinterFinishingsChanged"),
|
|
this,
|
|
SIGNAL(PrinterFinishingsChanged(QString,QString,QString,uint,QString,bool)));
|
|
|
|
// Job related signals
|
|
// JobState
|
|
notifierConnect(QLatin1String("JobState"),
|
|
this,
|
|
SIGNAL(jobState(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
|
|
|
|
// JobCreated
|
|
notifierConnect(QLatin1String("JobCreated"),
|
|
this,
|
|
SIGNAL(jobCreated(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
|
|
|
|
// JobStopped
|
|
notifierConnect(QLatin1String("JobStopped"),
|
|
this,
|
|
SIGNAL(jobStopped(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
|
|
|
|
// JobConfigChanged
|
|
notifierConnect(QLatin1String("JobConfigChanged"),
|
|
this,
|
|
SIGNAL(jobConfigChanged(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
|
|
|
|
// JobProgress
|
|
notifierConnect(QLatin1String("JobProgress"),
|
|
this,
|
|
SIGNAL(jobProgress(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
|
|
|
|
// JobCompleted
|
|
notifierConnect(QLatin1String("JobCompleted"),
|
|
this,
|
|
SIGNAL(jobCompleted(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
|
|
|
|
// This signal is needed since the cups registration thing
|
|
// doesn't emit printerAdded when we add a printer class
|
|
// This is emitted when a printer/queue is changed
|
|
QDBusConnection::systemBus().connect(QLatin1String(""),
|
|
QLatin1String("/com/redhat/PrinterSpooler"),
|
|
QLatin1String("com.redhat.PrinterSpooler"),
|
|
QLatin1String("PrinterAdded"),
|
|
this,
|
|
SIGNAL(rhPrinterAdded(QString)));
|
|
|
|
// This signal is needed since the cups registration thing
|
|
// sometimes simple stops working... don't ask me why
|
|
// This is emitted when a printer/queue is changed
|
|
QDBusConnection::systemBus().connect(QLatin1String(""),
|
|
QLatin1String("/com/redhat/PrinterSpooler"),
|
|
QLatin1String("com.redhat.PrinterSpooler"),
|
|
QLatin1String("QueueChanged"),
|
|
this,
|
|
SIGNAL(rhQueueChanged(QString)));
|
|
|
|
// This signal is needed since the cups registration thing
|
|
// doesn't emit printerRemoved when we add a printer class
|
|
// This is emitted when a printer/queue is changed
|
|
QDBusConnection::systemBus().connect(QLatin1String(""),
|
|
QLatin1String("/com/redhat/PrinterSpooler"),
|
|
QLatin1String("com.redhat.PrinterSpooler"),
|
|
QLatin1String("PrinterRemoved"),
|
|
this,
|
|
SIGNAL(rhPrinterRemoved(QString)));
|
|
|
|
QDBusConnection::systemBus().connect(QLatin1String(""),
|
|
QLatin1String("/com/redhat/PrinterSpooler"),
|
|
QLatin1String("com.redhat.PrinterSpooler"),
|
|
QLatin1String("JobQueuedLocal"),
|
|
this,
|
|
SIGNAL(rhJobQueuedLocal(QString,uint,QString)));
|
|
|
|
QDBusConnection::systemBus().connect(QLatin1String(""),
|
|
QLatin1String("/com/redhat/PrinterSpooler"),
|
|
QLatin1String("com.redhat.PrinterSpooler"),
|
|
QLatin1String("JobStartedLocal"),
|
|
this,
|
|
SIGNAL(rhJobStartedLocal(QString,uint,QString)));
|
|
|
|
// Creates the timer that will renew the DBus subscription
|
|
m_renewTimer = new QTimer;
|
|
m_renewTimer->setInterval(RENEW_INTERVAL*1000);
|
|
m_renewTimer->moveToThread(this);
|
|
connect(m_renewTimer, SIGNAL(timeout()), this, SLOT(renewDBusSubscription()), Qt::DirectConnection);
|
|
|
|
// Creates the timer to merge updates on the DBus subscription
|
|
m_subscriptionTimer = new QTimer;
|
|
m_subscriptionTimer->setInterval(0);
|
|
m_subscriptionTimer->setSingleShot(true);
|
|
m_subscriptionTimer->moveToThread(this);
|
|
connect(m_subscriptionTimer, SIGNAL(timeout()), this, SLOT(updateSubscription()), Qt::DirectConnection);
|
|
|
|
// Starts this thread
|
|
start();
|
|
}
|
|
|
|
|
|
void KCupsConnection::run()
|
|
{
|
|
// Check if we need an special connection
|
|
if (!m_serverUrl.isEmpty()) {
|
|
if (m_serverUrl.port() < 0) {
|
|
// TODO find out if there's a better way of hardcoding
|
|
// the CUPS port
|
|
m_serverUrl.setPort(631);
|
|
}
|
|
|
|
cupsSetServer(m_serverUrl.authority().toUtf8());
|
|
}
|
|
|
|
// This is dead cool, cups will call the thread_password_cb()
|
|
// function when a password set is needed, as we passed the
|
|
// password dialog pointer the functions just need to call
|
|
// it on a blocking mode.
|
|
cupsSetPasswordCB2(password_cb, m_passwordDialog);
|
|
|
|
m_inited = true;
|
|
exec();
|
|
|
|
// Event loop quit so cancelDBusSubscription()
|
|
if (m_subscriptionId != -1) {
|
|
cancelDBusSubscription();
|
|
}
|
|
}
|
|
|
|
bool KCupsConnection::readyToStart()
|
|
{
|
|
if (QThread::currentThread() == this) {
|
|
password_retries = 0;
|
|
internalErrorCount = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
ReturnArguments KCupsConnection::request(const KIppRequest &request, ipp_tag_t groupTag) const
|
|
{
|
|
ReturnArguments ret;
|
|
ipp_t *response = NULL;
|
|
do {
|
|
ippDelete(response);
|
|
response = NULL;
|
|
|
|
response = request.sendIppRequest();
|
|
} while (retry(request.resource().toUtf8(), request.operation()));
|
|
|
|
if (response && groupTag != IPP_TAG_ZERO) {
|
|
ret = parseIPPVars(response, groupTag);
|
|
}
|
|
ippDelete(response);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int KCupsConnection::renewDBusSubscription(int subscriptionId, int leaseDuration, const QStringList &events)
|
|
{
|
|
int ret = -1;
|
|
|
|
ipp_op_t operation;
|
|
|
|
// check if we have a valid subscription ID
|
|
if (subscriptionId >= 0) {
|
|
// Add the "notify-events" values to the request
|
|
operation = IPP_RENEW_SUBSCRIPTION;
|
|
} else {
|
|
operation = IPP_CREATE_PRINTER_SUBSCRIPTION;
|
|
}
|
|
|
|
KIppRequest request(operation, "/");
|
|
request.addString(IPP_TAG_OPERATION, IPP_TAG_URI,
|
|
KCUPS_PRINTER_URI, QLatin1String("/"));
|
|
request.addInteger(IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
|
|
KCUPS_NOTIFY_LEASE_DURATION, leaseDuration);
|
|
|
|
if (operation == IPP_CREATE_PRINTER_SUBSCRIPTION) {
|
|
// Add the "notify-events" values to the request
|
|
request.addStringList(IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
|
|
KCUPS_NOTIFY_EVENTS, events);
|
|
request.addString(IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
|
|
KCUPS_NOTIFY_PULL_METHOD, "ippget");
|
|
request.addString(IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
|
|
KCUPS_NOTIFY_RECIPIENT_URI, "dbus://");
|
|
} else {
|
|
request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER,
|
|
KCUPS_NOTIFY_SUBSCRIPTION_ID, subscriptionId);
|
|
}
|
|
|
|
ipp_t *response = NULL;
|
|
do {
|
|
// Do the request
|
|
response = request.sendIppRequest();
|
|
} while (retry("/", operation));
|
|
|
|
#if !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6)
|
|
if (response && ippGetStatusCode(response) == IPP_OK) {
|
|
#else
|
|
if (response && response->request.status.status_code == IPP_OK) {
|
|
#endif // !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6)
|
|
ipp_attribute_t *attr;
|
|
if (subscriptionId >= 0) {
|
|
// Request was ok, just return the current subscription
|
|
ret = subscriptionId;
|
|
} else if ((attr = ippFindAttribute(response,
|
|
"notify-subscription-id",
|
|
IPP_TAG_INTEGER)) == NULL) {
|
|
kWarning() << "No notify-subscription-id in response!";
|
|
ret = -1;
|
|
} else {
|
|
#if !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6)
|
|
ret = ippGetInteger(attr, 0);
|
|
}
|
|
} else if (subscriptionId >= 0 && response && ippGetStatusCode(response) == IPP_NOT_FOUND) {
|
|
kDebug() << "Subscription not found";
|
|
// When the subscription is not found try to get a new one
|
|
return renewDBusSubscription(-1, leaseDuration, events);
|
|
#else
|
|
ret = attr->values[0].integer;
|
|
}
|
|
} else if (subscriptionId >= 0 && response && response->request.status.status_code == IPP_NOT_FOUND) {
|
|
kDebug() << "Subscription not found";
|
|
// When the subscription is not found try to get a new one
|
|
return renewDBusSubscription(-1, leaseDuration, events);
|
|
#endif // !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6)
|
|
} else {
|
|
kDebug() << "Request failed" << cupsLastError() << httpGetStatus(CUPS_HTTP_DEFAULT);
|
|
// When the server stops/restarts we will have some error so ignore it
|
|
ret = subscriptionId;
|
|
}
|
|
|
|
ippDelete(response);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void KCupsConnection::notifierConnect(const QString &signal, QObject *receiver, const char *slot)
|
|
{
|
|
QDBusConnection systemBus = QDBusConnection::systemBus();
|
|
systemBus.connect(QString(),
|
|
QLatin1String("/org/cups/cupsd/Notifier"),
|
|
QLatin1String("org.cups.cupsd.Notifier"),
|
|
signal,
|
|
receiver,
|
|
slot);
|
|
}
|
|
|
|
void KCupsConnection::connectNotify(const char *signal)
|
|
{
|
|
QString event = eventForSignal(signal);
|
|
if (!event.isNull()) {
|
|
m_connectedEvents << event;
|
|
QMetaObject::invokeMethod(m_subscriptionTimer,
|
|
"start",
|
|
Qt::QueuedConnection);
|
|
}
|
|
}
|
|
|
|
void KCupsConnection::disconnectNotify(const char *signal)
|
|
{
|
|
QString event = eventForSignal(signal);
|
|
if (!event.isNull()) {
|
|
m_connectedEvents.removeOne(event);
|
|
QMetaObject::invokeMethod(m_subscriptionTimer,
|
|
"start",
|
|
Qt::QueuedConnection);
|
|
}
|
|
}
|
|
|
|
QString KCupsConnection::eventForSignal(const char *signal) const
|
|
{
|
|
// Server signals
|
|
if (QLatin1String(signal) == SIGNAL(serverAudit(QString))) {
|
|
return DBUS_SERVER_AUDIT;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(serverStarted(QString))) {
|
|
return DBUS_SERVER_STARTED;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(serverStopped(QString))) {
|
|
return DBUS_SERVER_STOPPED;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(serverRestarted(QString))) {
|
|
return DBUS_SERVER_RESTARTED;
|
|
}
|
|
|
|
// Printer signals
|
|
if (QLatin1String(signal) == SIGNAL(printerAdded(QString,QString,QString,uint,QString,bool))) {
|
|
return DBUS_PRINTER_ADDED;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(printerDeleted(QString,QString,QString,uint,QString,bool))) {
|
|
return DBUS_PRINTER_DELETED;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(printerFinishingsChanged(QString,QString,QString,uint,QString,bool))) {
|
|
return DBUS_PRINTER_FINISHINGS_CHANGED;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(printerMediaChanged(QString,QString,QString,uint,QString,bool))) {
|
|
return DBUS_PRINTER_MEDIA_CHANGED;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(printerModified(QString,QString,QString,uint,QString,bool))) {
|
|
return DBUS_PRINTER_MODIFIED;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(printerRestarted(QString,QString,QString,uint,QString,bool))) {
|
|
return DBUS_PRINTER_RESTARTED;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(printerShutdown(QString,QString,QString,uint,QString,bool))) {
|
|
return DBUS_PRINTER_SHUTDOWN;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(printerStateChanged(QString,QString,QString,uint,QString,bool))) {
|
|
return DBUS_PRINTER_STATE_CHANGED;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(printerStopped(QString,QString,QString,uint,QString,bool))) {
|
|
return DBUS_PRINTER_STOPPED;
|
|
}
|
|
|
|
// job signals
|
|
if (QLatin1String(signal) == SIGNAL(jobCompleted(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint))) {
|
|
return DBUS_JOB_COMPLETED;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(jobConfigChanged(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint))) {
|
|
return DBUS_JOB_CONFIG_CHANGED;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(jobCreated(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint))) {
|
|
return DBUS_JOB_CREATED;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(jobProgress(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint))) {
|
|
return DBUS_JOB_PROGRESS;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(jobState(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint))) {
|
|
return DBUS_JOB_STATE_CHANGED;
|
|
}
|
|
if (QLatin1String(signal) == SIGNAL(jobStopped(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint))) {
|
|
return DBUS_JOB_STOPPED;
|
|
}
|
|
|
|
// No registered event signal matched
|
|
return QString();
|
|
}
|
|
|
|
void KCupsConnection::updateSubscription()
|
|
{
|
|
// Build the current list
|
|
QStringList currentEvents = m_connectedEvents;
|
|
currentEvents.sort();
|
|
currentEvents.removeDuplicates();
|
|
|
|
// Check if the requested events are already being asked
|
|
if (m_requestedDBusEvents != currentEvents) {
|
|
m_requestedDBusEvents = currentEvents;
|
|
|
|
// If we alread have a subscription lets cancel
|
|
// and create a new one
|
|
if (m_subscriptionId >= 0) {
|
|
cancelDBusSubscription();
|
|
}
|
|
|
|
// Canculates the new events
|
|
renewDBusSubscription();
|
|
}
|
|
}
|
|
|
|
void KCupsConnection::renewDBusSubscription()
|
|
{
|
|
// check if we have a valid subscription ID
|
|
if (m_subscriptionId >= 0) {
|
|
m_subscriptionId = renewDBusSubscription(m_subscriptionId, SUBSCRIPTION_DURATION);
|
|
}
|
|
|
|
// The above request might fail if the subscription was cancelled
|
|
if (m_subscriptionId < 0) {
|
|
if (m_requestedDBusEvents.isEmpty()) {
|
|
m_renewTimer->stop();
|
|
} else {
|
|
m_subscriptionId = renewDBusSubscription(m_subscriptionId, SUBSCRIPTION_DURATION, m_requestedDBusEvents);
|
|
m_renewTimer->start();
|
|
}
|
|
}
|
|
}
|
|
|
|
void KCupsConnection::cancelDBusSubscription()
|
|
{
|
|
KIppRequest request(IPP_CANCEL_SUBSCRIPTION, "/");
|
|
request.addString(IPP_TAG_OPERATION, IPP_TAG_URI,
|
|
KCUPS_PRINTER_URI, "/");
|
|
request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER,
|
|
KCUPS_NOTIFY_SUBSCRIPTION_ID, m_subscriptionId);
|
|
|
|
do {
|
|
// Do the request
|
|
ippDelete(request.sendIppRequest());
|
|
} while (retry(request.resource().toUtf8(), request.operation()));
|
|
|
|
// Reset the subscription id
|
|
m_subscriptionId = -1;
|
|
}
|
|
|
|
ReturnArguments KCupsConnection::parseIPPVars(ipp_t *response, ipp_tag_t group_tag)
|
|
{
|
|
ipp_attribute_t *attr;
|
|
ReturnArguments ret;
|
|
|
|
#if !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6)
|
|
QVariantHash destAttributes;
|
|
for (attr = ippFirstAttribute(response); attr != NULL; attr = ippNextAttribute(response)) {
|
|
// We hit an attribute sepparator
|
|
if (ippGetName(attr) == NULL) {
|
|
ret << destAttributes;
|
|
destAttributes.clear();
|
|
continue;
|
|
}
|
|
|
|
// Skip leading attributes until we hit a a group which can be a printer, job...
|
|
if (ippGetGroupTag(attr) != group_tag ||
|
|
(ippGetValueTag(attr) != IPP_TAG_INTEGER &&
|
|
ippGetValueTag(attr) != IPP_TAG_ENUM &&
|
|
ippGetValueTag(attr) != IPP_TAG_BOOLEAN &&
|
|
ippGetValueTag(attr) != IPP_TAG_TEXT &&
|
|
ippGetValueTag(attr) != IPP_TAG_TEXTLANG &&
|
|
ippGetValueTag(attr) != IPP_TAG_LANGUAGE &&
|
|
ippGetValueTag(attr) != IPP_TAG_NAME &&
|
|
ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
|
|
ippGetValueTag(attr) != IPP_TAG_KEYWORD &&
|
|
ippGetValueTag(attr) != IPP_TAG_RANGE &&
|
|
ippGetValueTag(attr) != IPP_TAG_URI)) {
|
|
continue;
|
|
}
|
|
|
|
// Add a printer description attribute...
|
|
destAttributes[QString::fromUtf8(ippGetName(attr))] = ippAttrToVariant(attr);
|
|
}
|
|
|
|
if (!destAttributes.isEmpty()) {
|
|
ret << destAttributes;
|
|
}
|
|
#else
|
|
for (attr = response->attrs; attr != NULL; attr = attr->next) {
|
|
/*
|
|
* Skip leading attributes until we hit a a group which can be a printer, job...
|
|
*/
|
|
while (attr && attr->group_tag != group_tag) {
|
|
attr = attr->next;
|
|
}
|
|
|
|
if (attr == NULL) {
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Pull the needed attributes from this printer...
|
|
*/
|
|
QVariantHash destAttributes;
|
|
for (; attr && attr->group_tag == group_tag; attr = attr->next) {
|
|
if (attr->value_tag != IPP_TAG_INTEGER &&
|
|
attr->value_tag != IPP_TAG_ENUM &&
|
|
attr->value_tag != IPP_TAG_BOOLEAN &&
|
|
attr->value_tag != IPP_TAG_TEXT &&
|
|
attr->value_tag != IPP_TAG_TEXTLANG &&
|
|
attr->value_tag != IPP_TAG_LANGUAGE &&
|
|
attr->value_tag != IPP_TAG_NAME &&
|
|
attr->value_tag != IPP_TAG_NAMELANG &&
|
|
attr->value_tag != IPP_TAG_KEYWORD &&
|
|
attr->value_tag != IPP_TAG_RANGE &&
|
|
attr->value_tag != IPP_TAG_URI) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Add a printer description attribute...
|
|
*/
|
|
destAttributes[QString::fromUtf8(attr->name)] = ippAttrToVariant(attr);
|
|
}
|
|
|
|
ret << destAttributes;
|
|
|
|
if (attr == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
#endif // !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6)
|
|
|
|
return ret;
|
|
}
|
|
|
|
QVariant KCupsConnection::ippAttrToVariant(ipp_attribute_t *attr)
|
|
{
|
|
QVariant ret;
|
|
#if !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6)
|
|
switch (ippGetValueTag(attr)) {
|
|
case IPP_TAG_INTEGER:
|
|
case IPP_TAG_ENUM:
|
|
if (ippGetCount(attr) == 1) {
|
|
ret = ippGetInteger(attr, 0);
|
|
} else {
|
|
QList<int> values;
|
|
for (int i = 0; i < ippGetCount(attr); ++i) {
|
|
values << ippGetInteger(attr, i);
|
|
}
|
|
ret = qVariantFromValue(values);
|
|
}
|
|
break;
|
|
case IPP_TAG_BOOLEAN:
|
|
if (ippGetCount(attr)== 1) {
|
|
ret = ippGetBoolean(attr, 0);
|
|
} else {
|
|
QList<bool> values;
|
|
for (int i = 0; i < ippGetCount(attr); ++i) {
|
|
values << ippGetBoolean(attr, i);
|
|
}
|
|
ret = qVariantFromValue(values);
|
|
}
|
|
break;
|
|
case IPP_TAG_RANGE:
|
|
{
|
|
QVariantList values;
|
|
for (int i = 0; i < ippGetCount(attr); ++i) {
|
|
int rangeUpper;
|
|
values << ippGetRange(attr, i, &rangeUpper);
|
|
values << rangeUpper;
|
|
}
|
|
ret = values;
|
|
}
|
|
break;
|
|
default:
|
|
if (ippGetCount(attr)== 1) {
|
|
ret = QString::fromUtf8(ippGetString(attr, 0, NULL));
|
|
} else {
|
|
QStringList values;
|
|
for (int i = 0; i < ippGetCount(attr); ++i) {
|
|
values << QString::fromUtf8(ippGetString(attr, i, NULL));
|
|
}
|
|
ret = values;
|
|
}
|
|
}
|
|
#else
|
|
switch (attr->value_tag) {
|
|
case IPP_TAG_INTEGER:
|
|
case IPP_TAG_ENUM:
|
|
if (attr->num_values == 1) {
|
|
ret = attr->values[0].integer;
|
|
} else {
|
|
QList<int> values;
|
|
for (int i = 0; i < attr->num_values; ++i) {
|
|
values << attr->values[i].integer;
|
|
}
|
|
ret = qVariantFromValue(values);
|
|
}
|
|
break;
|
|
case IPP_TAG_BOOLEAN:
|
|
if (attr->num_values == 1) {
|
|
ret = static_cast<bool>(attr->values[0].integer);
|
|
} else {
|
|
QList<bool> values;
|
|
for (int i = 0; i < attr->num_values; ++i) {
|
|
values << static_cast<bool>(attr->values[i].integer);
|
|
}
|
|
ret = qVariantFromValue(values);
|
|
}
|
|
break;
|
|
case IPP_TAG_RANGE:
|
|
{
|
|
QVariantList values;
|
|
for (int i = 0; i < attr->num_values; ++i) {
|
|
values << attr->values[i].range.lower;
|
|
values << attr->values[i].range.upper;
|
|
}
|
|
ret = values;
|
|
}
|
|
break;
|
|
default:
|
|
if (attr->num_values == 1) {
|
|
ret = QString::fromUtf8(attr->values[0].string.text);
|
|
} else {
|
|
QStringList values;
|
|
for (int i = 0; i < attr->num_values; ++i) {
|
|
values << QString::fromUtf8(attr->values[i].string.text);
|
|
}
|
|
ret = values;
|
|
}
|
|
}
|
|
#endif // !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6)
|
|
return ret;
|
|
}
|
|
|
|
bool KCupsConnection::retry(const char *resource, int operation) const
|
|
{
|
|
ipp_status_t status = cupsLastError();
|
|
|
|
if (operation != -1) {
|
|
kDebug() << ippOpString(static_cast<ipp_op_t>(operation)) << "last error:" << status << cupsLastErrorString();
|
|
} else {
|
|
kDebug() << operation << "last error:" << status << cupsLastErrorString();
|
|
}
|
|
|
|
// When CUPS process stops our connection
|
|
// with it fails and has to be re-established
|
|
if (status == IPP_INTERNAL_ERROR) {
|
|
// Deleting this connection thread forces it
|
|
// to create a new CUPS connection
|
|
kWarning() << "IPP_INTERNAL_ERROR: clearing cookies and reconnecting";
|
|
|
|
// TODO maybe reconnect is enough
|
|
// httpClearCookie(CUPS_HTTP_DEFAULT);
|
|
|
|
// Reconnect to CUPS
|
|
if (httpReconnect(CUPS_HTTP_DEFAULT)) {
|
|
kWarning() << "Failed to reconnect" << cupsLastErrorString();
|
|
|
|
// Server might be restarting sleep for a few ms
|
|
msleep(500);
|
|
}
|
|
|
|
// Try the request again
|
|
return ++internalErrorCount < 3;
|
|
}
|
|
|
|
bool forceAuth = false;
|
|
// If our user is forbidden to perform the
|
|
// task we try again using the root user
|
|
// ONLY if it was the first time
|
|
if (status == IPP_FORBIDDEN &&
|
|
password_retries == 0) {
|
|
// Pretend to be the root user
|
|
// Sometimes setting this just works
|
|
cupsSetUser("root");
|
|
|
|
// force authentication
|
|
forceAuth = true;
|
|
}
|
|
|
|
if (status == IPP_NOT_AUTHORIZED ||
|
|
status == IPP_NOT_AUTHENTICATED) {
|
|
if (password_retries > 3 || password_retries == -1) {
|
|
// the authentication failed 3 times
|
|
// OR the dialog was canceld (-1)
|
|
// reset to 0 and quit the do-while loop
|
|
password_retries = 0;
|
|
return false;
|
|
}
|
|
|
|
// force authentication
|
|
forceAuth = true;
|
|
}
|
|
|
|
if (forceAuth) {
|
|
// force authentication
|
|
kDebug() << "Calling cupsDoAuthentication() password_retries:" << password_retries;
|
|
int ret = cupsDoAuthentication(CUPS_HTTP_DEFAULT, "POST", resource);
|
|
kDebug() << "Called cupsDoAuthentication(), success:" << (ret == -1 ? true : false);
|
|
|
|
// If the authentication was succefull
|
|
// sometimes just trying to be root works
|
|
return ret == -1 ? true : false;
|
|
}
|
|
|
|
// the action was not forbidden
|
|
return false;
|
|
}
|
|
|
|
const char * password_cb(const char *prompt, http_t *http, const char *method, const char *resource, void *user_data)
|
|
{
|
|
Q_UNUSED(prompt)
|
|
Q_UNUSED(http)
|
|
Q_UNUSED(method)
|
|
Q_UNUSED(resource)
|
|
|
|
if (++password_retries > 3) {
|
|
// cancel the authentication
|
|
cupsSetUser(NULL);
|
|
return NULL;
|
|
}
|
|
|
|
KCupsPasswordDialog *passwordDialog = static_cast<KCupsPasswordDialog *>(user_data);
|
|
bool wrongPassword = password_retries > 1;
|
|
|
|
// This will block this thread until exec is not finished
|
|
kDebug() << password_retries;
|
|
QMetaObject::invokeMethod(passwordDialog,
|
|
"exec",
|
|
Qt::BlockingQueuedConnection,
|
|
Q_ARG(QString, QString::fromUtf8(cupsUser())),
|
|
Q_ARG(bool, wrongPassword));
|
|
kDebug() << passwordDialog->accepted();
|
|
|
|
// The password dialog has just returned check the result
|
|
// method that returns QDialog enums
|
|
if (passwordDialog->accepted()) {
|
|
cupsSetUser(passwordDialog->username().toUtf8());
|
|
return passwordDialog->password().toUtf8();
|
|
} else {
|
|
// the dialog was canceled
|
|
password_retries = -1;
|
|
cupsSetUser(NULL);
|
|
return NULL;
|
|
}
|
|
}
|