1
/***************************************************************************
2
* Copyright (C) 2010-2012 by Daniel Nicoletti *
4
* Copyright (C) 2012 Harald Sitter <sitter@kde.org> *
6
* This program is free software; you can redistribute it and/or modify *
7
* it under the terms of the GNU General Public License as published by *
8
* the Free Software Foundation; either version 2 of the License, or *
9
* (at your option) any later version. *
11
* This program is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU General Public License for more details. *
16
* You should have received a copy of the GNU General Public License *
17
* along with this program; see the file COPYING. If not, write to *
18
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
19
* Boston, MA 02110-1301, USA. *
20
***************************************************************************/
22
#include "KCupsConnection.h"
24
#include "KCupsPasswordDialog.h"
28
#include <QCoreApplication>
29
#include <QStringBuilder>
30
#include <QDBusConnection>
36
#include <cups/cups.h>
38
#define RENEW_INTERVAL 3500
39
#define SUBSCRIPTION_DURATION 3600
41
Q_DECLARE_METATYPE(QList<int>)
42
Q_DECLARE_METATYPE(QList<bool>)
44
KCupsConnection* KCupsConnection::m_instance = 0;
45
static int password_retries = 0;
46
static int internalErrorCount = 0;
47
const char * password_cb(const char *prompt, http_t *http, const char *method, const char *resource, void *user_data);
49
static const char **qStringListToCharPtrPtr(const QStringList &list, QList<QByteArray> *qbaList)
51
const char **ptr = new const char *[list.size() + 1];
52
qbaList->reserve(qbaList->size() + list.size());
54
for (int i = 0; i < list.size(); ++i) {
55
qba = list.at(i).toUtf8();
57
ptr[i] = qba.constData();
63
KCupsConnection* KCupsConnection::global()
66
m_instance = new KCupsConnection(qApp);
72
KCupsConnection::KCupsConnection(QObject *parent) :
75
// Creating the dialog before start() will make it run on the gui thread
76
m_passwordDialog(new KCupsPasswordDialog),
79
// setup the DBus subscriptions
81
// Server related signals
83
notifierConnect(QLatin1String("ServerStarted"),
85
SIGNAL(serverStarted(QString)));
88
notifierConnect(QLatin1String("ServerStopped"),
90
SIGNAL(serverStopped(QString)));
93
notifierConnect(QLatin1String("ServerRestarted"),
95
SIGNAL(serverRestarted(QString)));
98
notifierConnect(QLatin1String("ServerAudit"),
100
SIGNAL(serverAudit(QString)));
102
// Printer related signals
104
notifierConnect(QLatin1String("PrinterAdded"),
106
SIGNAL(printerAdded(QString,QString,QString,uint,QString,bool)));
109
notifierConnect(QLatin1String("PrinterModified"),
111
SIGNAL(printerModified(QString,QString,QString,uint,QString,bool)));
114
notifierConnect(QLatin1String("PrinterDeleted"),
116
SIGNAL(printerDeleted(QString,QString,QString,uint,QString,bool)));
118
// PrinterStateChanged
119
notifierConnect(QLatin1String("PrinterStateChanged"),
121
SIGNAL(printerStateChanged(QString,QString,QString,uint,QString,bool)));
124
notifierConnect(QLatin1String("PrinterStopped"),
126
SIGNAL(printerStopped(QString,QString,QString,uint,QString,bool)));
129
notifierConnect(QLatin1String("PrinterShutdown"),
131
SIGNAL(printerShutdown(QString,QString,QString,uint,QString,bool)));
134
notifierConnect(QLatin1String("PrinterRestarted"),
136
SIGNAL(printerRestarted(QString,QString,QString,uint,QString,bool)));
138
// PrinterMediaChanged
139
notifierConnect(QLatin1String("PrinterMediaChanged"),
141
SIGNAL(printerMediaChanged(QString,QString,QString,uint,QString,bool)));
143
// PrinterFinishingsChanged
144
notifierConnect(QLatin1String("PrinterFinishingsChanged"),
146
SIGNAL(PrinterFinishingsChanged(QString,QString,QString,uint,QString,bool)));
148
// Job related signals
150
notifierConnect(QLatin1String("JobState"),
152
SIGNAL(jobState(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
155
notifierConnect(QLatin1String("JobCreated"),
157
SIGNAL(jobCreated(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
160
notifierConnect(QLatin1String("JobStopped"),
162
SIGNAL(jobStopped(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
165
notifierConnect(QLatin1String("JobConfigChanged"),
167
SIGNAL(jobConfigChanged(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
170
notifierConnect(QLatin1String("JobProgress"),
172
SIGNAL(jobProgress(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
175
notifierConnect(QLatin1String("JobCompleted"),
177
SIGNAL(jobCompleted(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
179
// This signal is needed since the cups registration thing
180
// doesn't emit printerAdded when we add a printer class
181
// This is emitted when a printer/queue is changed
182
QDBusConnection::systemBus().connect(QLatin1String(""),
183
QLatin1String("/com/redhat/PrinterSpooler"),
184
QLatin1String("com.redhat.PrinterSpooler"),
185
QLatin1String("PrinterAdded"),
187
SIGNAL(rhPrinterAdded(QString)));
189
// This signal is needed since the cups registration thing
190
// sometimes simple stops working... don't ask me why
191
// This is emitted when a printer/queue is changed
192
QDBusConnection::systemBus().connect(QLatin1String(""),
193
QLatin1String("/com/redhat/PrinterSpooler"),
194
QLatin1String("com.redhat.PrinterSpooler"),
195
QLatin1String("QueueChanged"),
197
SIGNAL(rhQueueChanged(QString)));
199
// This signal is needed since the cups registration thing
200
// doesn't emit printerRemoved when we add a printer class
201
// This is emitted when a printer/queue is changed
202
QDBusConnection::systemBus().connect(QLatin1String(""),
203
QLatin1String("/com/redhat/PrinterSpooler"),
204
QLatin1String("com.redhat.PrinterSpooler"),
205
QLatin1String("PrinterRemoved"),
207
SIGNAL(rhPrinterRemoved(QString)));
209
QDBusConnection::systemBus().connect(QLatin1String(""),
210
QLatin1String("/com/redhat/PrinterSpooler"),
211
QLatin1String("com.redhat.PrinterSpooler"),
212
QLatin1String("JobQueuedLocal"),
214
SIGNAL(rhJobQueuedLocal(QString,uint,QString)));
216
QDBusConnection::systemBus().connect(QLatin1String(""),
217
QLatin1String("/com/redhat/PrinterSpooler"),
218
QLatin1String("com.redhat.PrinterSpooler"),
219
QLatin1String("JobStartedLocal"),
221
SIGNAL(rhJobStartedLocal(QString,uint,QString)));
223
// Starts this thread
227
KCupsConnection::~KCupsConnection()
230
if (m_subscriptionId != -1) {
231
cancelDBusSubscription();
233
m_renewTimer->deleteLater();
234
m_passwordDialog->deleteLater();
240
void KCupsConnection::run()
242
// This is dead cool, cups will call the thread_password_cb()
243
// function when a password set is needed, as we passed the
244
// password dialog pointer the functions just need to call
245
// it on a blocking mode.
246
cupsSetPasswordCB2(password_cb, m_passwordDialog);
248
// Creates the timer that will renew the DBus subscription
249
m_renewTimer = new QTimer;
250
m_renewTimer->setInterval(RENEW_INTERVAL);
251
m_renewTimer->moveToThread(this);
252
connect(m_renewTimer, SIGNAL(timeout()), this, SLOT(renewDBusSubscription()), Qt::DirectConnection);
258
bool KCupsConnection::readyToStart()
260
if (QThread::currentThread() == KCupsConnection::global()) {
261
password_retries = 0;
262
internalErrorCount = 0;
268
ReturnArguments KCupsConnection::request(ipp_op_e operation,
269
const char *resource,
270
const QVariantHash &reqValues,
275
if (!readyToStart()) {
276
return ret; // This is not intended to be used in the gui thread
279
ipp_t *response = NULL;
280
bool needDestName = false;
281
int group_tag = IPP_TAG_PRINTER;
284
bool isClass = false;
286
QVariantHash values = reqValues;
290
if (values.contains(QLatin1String("printer-is-class"))) {
291
isClass = values.take(QLatin1String("printer-is-class")).toBool();
293
if (values.contains(QLatin1String("need-dest-name"))) {
294
needDestName = values.take(QLatin1String("need-dest-name")).toBool();
296
if (values.contains(QLatin1String("group-tag-qt"))) {
297
group_tag = values.take(QLatin1String("group-tag-qt")).toInt();
300
if (values.contains(QLatin1String("filename"))) {
301
filename = values.take(QLatin1String("filename")).toString();
304
// Lets create the request
305
if (values.contains(QLatin1String(KCUPS_PRINTER_NAME))) {
306
request = ippNewDefaultRequest(values.take(QLatin1String(KCUPS_PRINTER_NAME)).toString(),
310
request = ippNewRequest(operation);
313
// send our user name on the request too
314
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
315
"requesting-user-name", NULL, cupsUser());
317
// Add the requested values to the request
318
requestAddValues(request, values);
321
// do the request deleting the response
322
if (filename.isEmpty()) {
323
response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, resource);
325
response = cupsDoFileRequest(CUPS_HTTP_DEFAULT, request, resource, filename.toUtf8());
327
} while (retry(resource));
329
if (response != NULL && needResponse) {
330
ret = parseIPPVars(response, group_tag, needDestName);
337
int KCupsConnection::createDBusSubscription(const QStringList &events)
339
// Build the current list
340
QStringList currentEvents;
341
foreach (const QStringList ®isteredEvents, m_requestedDBusEvents) {
342
currentEvents << registeredEvents;
344
currentEvents.removeDuplicates();
346
// Check if the requested events are already being asked
348
foreach (const QString &event, events) {
349
if (!currentEvents.contains(event)) {
355
// Store the subscription
357
if (!m_requestedDBusEvents.isEmpty()) {
358
id = m_requestedDBusEvents.keys().last();
361
m_requestedDBusEvents[id] = events;
363
// If the requested list is included in our request just
369
// If we alread have a subscription lets cancel
370
// and create a new one
371
if (m_subscriptionId >= 0) {
372
cancelDBusSubscription();
375
// Canculates the new events
376
renewDBusSubscription();
381
void KCupsConnection::removeDBusSubscription(int subscriptionId)
383
// Collect the list of current events
384
QStringList currentEvents;
385
foreach (const QStringList ®isteredEvents, m_requestedDBusEvents) {
386
currentEvents << registeredEvents;
388
currentEvents.removeDuplicates();
390
QStringList removedEvents = m_requestedDBusEvents.take(subscriptionId);
392
// Check if the removed events list is the same as the list we
393
// need, if yes means we can keep renewing the same events
394
if (removedEvents == currentEvents && !m_requestedDBusEvents.isEmpty()) {
397
// The requested events changed
398
cancelDBusSubscription();
400
// Canculates the new events
401
renewDBusSubscription();
405
int KCupsConnection::renewDBusSubscription(int subscriptionId, int leaseDuration, const QStringList &events)
409
if (!readyToStart()) {
410
kWarning() << "Tryied to run on the wrong thread";
411
return subscriptionId; // This is not intended to be used in the gui thread
414
ipp_t *response = NULL;
419
// check if we have a valid subscription ID
420
if (subscriptionId >= 0) {
421
// Add the "notify-events" values to the request
422
operation = IPP_RENEW_SUBSCRIPTION;
424
operation = IPP_CREATE_PRINTER_SUBSCRIPTION;
427
// Lets create the request
428
request = ippNewRequest(operation);
429
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
430
KCUPS_PRINTER_URI, NULL, "/");
431
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
432
"requesting-user-name", NULL, cupsUser());
434
if (operation == IPP_CREATE_PRINTER_SUBSCRIPTION) {
435
// Add the "notify-events" values to the request
437
values["notify-events"] = events;
438
requestAddValues(request, values);
440
ippAddString(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
441
"notify-pull-method", NULL, "ippget");
442
ippAddString(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
443
"notify-recipient-uri", NULL, "dbus://");
444
ippAddInteger(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
445
"notify-lease-duration", leaseDuration);
447
ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
448
"notify-subscription-id", subscriptionId);
449
ippAddInteger(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
450
"notify-lease-duration", leaseDuration);
454
response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
455
} while (retry("/"));
458
if (response && ippGetStatusCode(response) == IPP_OK) {
460
if (response && response->request.status.status_code == IPP_OK) {
461
#endif // HAVE_CUPS_1_6
462
ipp_attribute_t *attr;
463
if (subscriptionId >= 0) {
464
// Request was ok, just return the current subscription
465
ret = subscriptionId;
466
} else if ((attr = ippFindAttribute(response,
467
"notify-subscription-id",
468
IPP_TAG_INTEGER)) == NULL) {
469
kWarning() << "No notify-subscription-id in response!";
473
ret = ippGetInteger(attr, 0);
475
ret = attr->values[0].integer;
476
#endif // HAVE_CUPS_1_6
479
kWarning() << "Request failed" << lastError();
488
void KCupsConnection::notifierConnect(const QString &signal, QObject *receiver, const char *slot)
490
QDBusConnection systemBus = QDBusConnection::systemBus();
491
systemBus.connect(QString(),
492
QLatin1String("/org/cups/cupsd/Notifier"),
493
QLatin1String("org.cups.cupsd.Notifier"),
499
void KCupsConnection::cancelDBusSubscription()
504
// Lets create the request
505
request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
506
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
507
KCUPS_PRINTER_URI, NULL, "/");
508
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
509
"requesting-user-name", NULL, cupsUser());
510
ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
511
"notify-subscription-id", m_subscriptionId);
514
ippDelete(cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/"));
515
} while (retry("/"));
517
// Reset the subscription id
518
m_subscriptionId = -1;
521
void KCupsConnection::renewDBusSubscription()
523
// check if we have a valid subscription ID
524
kDebug() << m_subscriptionId;
526
if (m_subscriptionId >= 0) {
527
m_subscriptionId = renewDBusSubscription(m_subscriptionId, SUBSCRIPTION_DURATION);
530
// The above request might fail if the subscription was cancelled
531
if (m_subscriptionId < 0) {
532
QStringList currentEvents;
533
foreach (const QStringList ®isteredEvents, m_requestedDBusEvents) {
534
currentEvents << registeredEvents;
536
currentEvents.removeDuplicates();
537
kDebug() << currentEvents;
539
if (!currentEvents.isEmpty()) {
540
m_subscriptionId = renewDBusSubscription(m_subscriptionId, SUBSCRIPTION_DURATION, currentEvents);
541
m_renewTimer->start();
543
m_renewTimer->stop();
548
void KCupsConnection::requestAddValues(ipp_t *request, const QVariantHash &values)
550
QVariantHash::const_iterator i = values.constBegin();
551
while (i != values.constEnd()) {
552
QString key = i.key();
553
QVariant value = i.value();
554
switch (value.type()) {
556
if (key == QLatin1String("printer-is-accepting-jobs")) {
557
ippAddBoolean(request, IPP_TAG_PRINTER,
558
"printer-is-accepting-jobs", value.toBool());
560
ippAddBoolean(request, IPP_TAG_OPERATION,
561
key.toUtf8(), value.toBool());
565
if (key == QLatin1String(KCUPS_JOB_ID)) {
566
ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
567
KCUPS_JOB_ID, value.toInt());
568
} else if (key == QLatin1String(KCUPS_PRINTER_STATE)) {
569
ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM,
570
KCUPS_PRINTER_STATE, IPP_PRINTER_IDLE);
572
ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
573
key.toUtf8(), value.toInt());
576
case QVariant::String:
577
if (key == QLatin1String(KCUPS_DEVICE_URI)) {
578
// device uri has a different TAG
579
ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI,
580
KCUPS_DEVICE_URI, "utf-8", value.toString().toUtf8());
581
} else if (key == QLatin1String("job-printer-uri")) {
582
// TODO this seems broken
583
const char* dest_name = value.toString().toUtf8();
584
char destUri[HTTP_MAX_URI];
585
httpAssembleURIf(HTTP_URI_CODING_ALL, destUri, sizeof(destUri),
586
"ipp", "utf-8", "localhost", ippPort(),
587
"/printers/%s", dest_name);
588
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
589
KCUPS_JOB_PRINTER_URI, "utf-8", value.toString().toUtf8());
590
} else if (key == QLatin1String(KCUPS_PRINTER_OP_POLICY) ||
591
key == QLatin1String(KCUPS_PRINTER_ERROR_POLICY) ||
592
key == QLatin1String("ppd-name")) {
593
// printer-op-policy has a different TAG
594
ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
595
key.toUtf8(), "utf-8", value.toString().toUtf8());
596
} else if (key == QLatin1String(KCUPS_JOB_NAME)) {
597
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
598
KCUPS_JOB_NAME, "utf-8", value.toString().toUtf8());
599
} else if (key == QLatin1String("which-jobs")) {
600
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
601
"which-jobs", "utf-8", value.toString().toUtf8());
603
ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
604
key.toUtf8(), "utf-8", value.toString().toUtf8());
607
case QVariant::StringList:
609
QStringList list = value.value<QStringList>();
611
QList<QByteArray> valuesQByteArrayList;
612
const char **values = qStringListToCharPtrPtr(list, &valuesQByteArrayList);
614
if (key == QLatin1String(KCUPS_MEMBER_URIS)) {
615
ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_URI,
616
KCUPS_MEMBER_URIS, list.size(), "utf-8", values);
617
} else if (key == QLatin1String("requested-attributes")) {
618
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
619
"requested-attributes", list.size(), "utf-8", values);
620
} else if (key == QLatin1String("notify-events")) {
621
// Used for DBus notification, the values contains
622
// what we want to watch
623
ippAddStrings(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
624
"notify-events", list.size(), NULL, values);
626
ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
627
i.key().toUtf8(), list.size(), "utf-8", values);
630
// ippAddStrings deep copies everything so we can throw away the values.
631
// the QBAList and content is auto discarded when going out of scope.
636
ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
637
key.toUtf8(), value.toInt());
640
kWarning() << "type NOT recognized! This will be ignored:" << key << "values" << value;
646
ReturnArguments KCupsConnection::parseIPPVars(ipp_t *response, int group_tag, bool needDestName)
648
ipp_attribute_t *attr;
652
QVariantHash destAttributes;
653
for (attr = ippFirstAttribute(response); attr != NULL; attr = ippNextAttribute(response)) {
654
// We hit an attribute sepparator
655
if (ippGetName(attr) == NULL) {
656
ret << destAttributes;
657
destAttributes.clear();
661
// Skip leading attributes until we hit a a group which can be a printer, job...
662
if (ippGetGroupTag(attr) != group_tag ||
663
(ippGetValueTag(attr) != IPP_TAG_INTEGER &&
664
ippGetValueTag(attr) != IPP_TAG_ENUM &&
665
ippGetValueTag(attr) != IPP_TAG_BOOLEAN &&
666
ippGetValueTag(attr) != IPP_TAG_TEXT &&
667
ippGetValueTag(attr) != IPP_TAG_TEXTLANG &&
668
ippGetValueTag(attr) != IPP_TAG_LANGUAGE &&
669
ippGetValueTag(attr) != IPP_TAG_NAME &&
670
ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
671
ippGetValueTag(attr) != IPP_TAG_KEYWORD &&
672
ippGetValueTag(attr) != IPP_TAG_RANGE &&
673
ippGetValueTag(attr) != IPP_TAG_URI)) {
677
// Add a printer description attribute...
678
destAttributes[QString::fromUtf8(ippGetName(attr))] = ippAttrToVariant(attr);
681
if (!destAttributes.isEmpty()) {
682
ret << destAttributes;
685
for (attr = response->attrs; attr != NULL; attr = attr->next) {
687
* Skip leading attributes until we hit a a group which can be a printer, job...
689
while (attr && attr->group_tag != group_tag) {
698
* Pull the needed attributes from this printer...
700
QVariantHash destAttributes;
701
for (; attr && attr->group_tag == group_tag; attr = attr->next) {
702
if (attr->value_tag != IPP_TAG_INTEGER &&
703
attr->value_tag != IPP_TAG_ENUM &&
704
attr->value_tag != IPP_TAG_BOOLEAN &&
705
attr->value_tag != IPP_TAG_TEXT &&
706
attr->value_tag != IPP_TAG_TEXTLANG &&
707
attr->value_tag != IPP_TAG_LANGUAGE &&
708
attr->value_tag != IPP_TAG_NAME &&
709
attr->value_tag != IPP_TAG_NAMELANG &&
710
attr->value_tag != IPP_TAG_KEYWORD &&
711
attr->value_tag != IPP_TAG_RANGE &&
712
attr->value_tag != IPP_TAG_URI) {
717
* Add a printer description attribute...
719
destAttributes[QString::fromUtf8(attr->name)] = ippAttrToVariant(attr);
723
* See if we have everything needed...
725
if (needDestName && destAttributes[QLatin1String(KCUPS_PRINTER_NAME)].toString().isEmpty()) {
733
ret << destAttributes;
739
#endif // HAVE_CUPS_1_6
744
// Don't forget to delete the request
745
ipp_t* KCupsConnection::ippNewDefaultRequest(const QString &name, bool isClass, ipp_op_t operation)
747
char uri[HTTP_MAX_URI]; // printer URI
752
destination = QLatin1String("/classes/") % name;
754
destination = QLatin1String("/printers/") % name;
757
// Create a new request
760
// * requesting-user-name
761
request = ippNewRequest(operation);
762
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", "utf-8", "localhost",
763
ippPort(), destination.toUtf8());
764
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, KCUPS_PRINTER_URI,
770
QVariant KCupsConnection::ippAttrToVariant(ipp_attribute_t *attr)
774
switch (ippGetValueTag(attr)) {
775
case IPP_TAG_INTEGER:
777
if (ippGetCount(attr) == 1) {
778
ret = ippGetInteger(attr, 0);
781
for (int i = 0; i < ippGetCount(attr); ++i) {
782
values << ippGetInteger(attr, i);
784
ret = qVariantFromValue(values);
787
case IPP_TAG_BOOLEAN:
788
if (ippGetCount(attr)== 1) {
789
ret = ippGetBoolean(attr, 0);
792
for (int i = 0; i < ippGetCount(attr); ++i) {
793
values << ippGetBoolean(attr, i);
795
ret = qVariantFromValue(values);
801
for (int i = 0; i < ippGetCount(attr); ++i) {
803
values << ippGetRange(attr, i, &rangeUpper);
804
values << rangeUpper;
810
if (ippGetCount(attr)== 1) {
811
ret = QString::fromUtf8(ippGetString(attr, 0, NULL));
814
for (int i = 0; i < ippGetCount(attr); ++i) {
815
values << QString::fromUtf8(ippGetString(attr, i, NULL));
821
switch (attr->value_tag) {
822
case IPP_TAG_INTEGER:
824
if (attr->num_values == 1) {
825
ret = attr->values[0].integer;
828
for (int i = 0; i < attr->num_values; ++i) {
829
values << attr->values[i].integer;
831
ret = qVariantFromValue(values);
834
case IPP_TAG_BOOLEAN:
835
if (attr->num_values == 1) {
836
ret = static_cast<bool>(attr->values[0].integer);
839
for (int i = 0; i < attr->num_values; ++i) {
840
values << static_cast<bool>(attr->values[i].integer);
842
ret = qVariantFromValue(values);
848
for (int i = 0; i < attr->num_values; ++i) {
849
values << attr->values[i].range.lower;
850
values << attr->values[i].range.upper;
856
if (attr->num_values == 1) {
857
ret = QString::fromUtf8(attr->values[0].string.text);
860
for (int i = 0; i < attr->num_values; ++i) {
861
values << QString::fromUtf8(attr->values[i].string.text);
866
#endif // HAVE_CUPS_1_6
870
bool KCupsConnection::retry(const char *resource)
872
ipp_status_t status = cupsLastError();
874
kDebug() << "cupsLastError():" << status << cupsLastErrorString();
876
// When CUPS process stops our connection
877
// with it fails and has to be re-established
878
if (status == IPP_INTERNAL_ERROR) {
879
// Deleting this connection thread forces it
880
// to create a new CUPS_HTTP_DEFAULT connection
881
kDebug() << "IPP_INTERNAL_ERROR clearing cookies";
882
httpClearCookie(CUPS_HTTP_DEFAULT);
884
// Server might be restarting sleep for a few ms
887
// Try the request again
888
return ++internalErrorCount < 3;
891
bool forceAuth = false;
892
// If our user is forbidden to perform the
893
// task we try again using the root user
894
// ONLY if it was the first time
895
if (status == IPP_FORBIDDEN &&
896
password_retries == 0) {
897
// Pretend to be the root user
898
// Sometimes setting this just works
901
// force authentication
905
if (status == IPP_NOT_AUTHORIZED ||
906
status == IPP_NOT_AUTHENTICATED) {
907
if (password_retries > 3 || password_retries == -1) {
908
// the authentication failed 3 times
909
// OR the dialog was canceld (-1)
910
// reset to 0 and quit the do-while loop
911
password_retries = 0;
915
// force authentication
920
// force authentication
921
kDebug() << "cupsDoAuthentication() password_retries:" << password_retries;
922
int ret = cupsDoAuthentication(CUPS_HTTP_DEFAULT, "POST", resource);
923
kDebug() << "cupsDoAuthentication() success:" << (ret == -1 ? true : false);
925
// If the authentication was succefull
926
// sometimes just trying to be root works
927
return ret == -1 ? true : false;
930
// the action was not forbidden
934
ipp_status_t KCupsConnection::lastError()
936
ipp_status_t status = cupsLastError();
941
const char * password_cb(const char *prompt, http_t *http, const char *method, const char *resource, void *user_data)
948
if (++password_retries > 3) {
949
// cancel the authentication
954
KCupsPasswordDialog *passwordDialog = static_cast<KCupsPasswordDialog *>(user_data);
955
bool wrongPassword = password_retries > 1;
957
// This will block this thread until exec is not finished
958
kDebug() << password_retries;
959
QMetaObject::invokeMethod(passwordDialog,
961
Qt::BlockingQueuedConnection,
962
Q_ARG(QString, QString::fromUtf8(cupsUser())),
963
Q_ARG(bool, wrongPassword));
964
kDebug() << passwordDialog->accepted();
966
// The password dialog has just returned check the result
967
// method that returns QDialog enums
968
if (passwordDialog->accepted()) {
969
cupsSetUser(passwordDialog->username().toUtf8());
970
return passwordDialog->password().toUtf8();
972
// the dialog was canceled
973
password_retries = -1;