~ubuntu-branches/ubuntu/quantal/print-manager/quantal-proposed

« back to all changes in this revision

Viewing changes to .pc/kubuntu_02_connection_log_spam.diff/libkcups/KCupsConnection.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2012-12-17 15:37:12 UTC
  • Revision ID: package-import@ubuntu.com-20121217153712-yc8d5vgqph8dldsc
Tags: 0.2.0-0ubuntu4
Add kubuntu_02_connection_log_spam.diff from upstream, fixes
LP: #1091340 "print-manager spams .xsession-errors"

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   Copyright (C) 2010-2012 by Daniel Nicoletti                           *
 
3
 *   dantti12@gmail.com                                                    *
 
4
 *   Copyright (C) 2012 Harald Sitter <sitter@kde.org>                     *
 
5
 *                                                                         *
 
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.                                   *
 
10
 *                                                                         *
 
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.                          *
 
15
 *                                                                         *
 
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
 ***************************************************************************/
 
21
 
 
22
#include "KCupsConnection.h"
 
23
 
 
24
#include "KCupsPasswordDialog.h"
 
25
 
 
26
#include <config.h>
 
27
 
 
28
#include <QCoreApplication>
 
29
#include <QStringBuilder>
 
30
#include <QDBusConnection>
 
31
#include <QByteArray>
 
32
 
 
33
#include <KLocale>
 
34
#include <KDebug>
 
35
 
 
36
#include <cups/cups.h>
 
37
 
 
38
#define RENEW_INTERVAL        3500
 
39
#define SUBSCRIPTION_DURATION 3600
 
40
 
 
41
Q_DECLARE_METATYPE(QList<int>)
 
42
Q_DECLARE_METATYPE(QList<bool>)
 
43
 
 
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);
 
48
 
 
49
static const char **qStringListToCharPtrPtr(const QStringList &list, QList<QByteArray> *qbaList)
 
50
{
 
51
    const char **ptr = new const char *[list.size() + 1];
 
52
    qbaList->reserve(qbaList->size() + list.size());
 
53
    QByteArray qba;
 
54
    for (int i = 0; i < list.size(); ++i) {
 
55
        qba = list.at(i).toUtf8();
 
56
        qbaList->append(qba);
 
57
        ptr[i] = qba.constData();
 
58
    }
 
59
    ptr[list.size()] = 0;
 
60
    return ptr;
 
61
}
 
62
 
 
63
KCupsConnection* KCupsConnection::global()
 
64
{
 
65
    if (!m_instance) {
 
66
        m_instance = new KCupsConnection(qApp);
 
67
    }
 
68
 
 
69
    return m_instance;
 
70
}
 
71
 
 
72
KCupsConnection::KCupsConnection(QObject *parent) :
 
73
    QThread(parent),
 
74
    m_inited(false),
 
75
    // Creating the dialog before start() will make it run on the gui thread
 
76
    m_passwordDialog(new KCupsPasswordDialog),
 
77
    m_subscriptionId(-1)
 
78
{
 
79
    // setup the DBus subscriptions
 
80
 
 
81
    // Server related signals
 
82
    // ServerStarted
 
83
    notifierConnect(QLatin1String("ServerStarted"),
 
84
                    this,
 
85
                    SIGNAL(serverStarted(QString)));
 
86
 
 
87
    // ServerStopped
 
88
    notifierConnect(QLatin1String("ServerStopped"),
 
89
                    this,
 
90
                    SIGNAL(serverStopped(QString)));
 
91
 
 
92
    // ServerRestarted
 
93
    notifierConnect(QLatin1String("ServerRestarted"),
 
94
                    this,
 
95
                    SIGNAL(serverRestarted(QString)));
 
96
 
 
97
    // ServerAudit
 
98
    notifierConnect(QLatin1String("ServerAudit"),
 
99
                    this,
 
100
                    SIGNAL(serverAudit(QString)));
 
101
 
 
102
    // Printer related signals
 
103
    // PrinterAdded
 
104
    notifierConnect(QLatin1String("PrinterAdded"),
 
105
                    this,
 
106
                    SIGNAL(printerAdded(QString,QString,QString,uint,QString,bool)));
 
107
 
 
108
    // PrinterModified
 
109
    notifierConnect(QLatin1String("PrinterModified"),
 
110
                    this,
 
111
                    SIGNAL(printerModified(QString,QString,QString,uint,QString,bool)));
 
112
 
 
113
    // PrinterDeleted
 
114
    notifierConnect(QLatin1String("PrinterDeleted"),
 
115
                    this,
 
116
                    SIGNAL(printerDeleted(QString,QString,QString,uint,QString,bool)));
 
117
 
 
118
    // PrinterStateChanged
 
119
    notifierConnect(QLatin1String("PrinterStateChanged"),
 
120
                    this,
 
121
                    SIGNAL(printerStateChanged(QString,QString,QString,uint,QString,bool)));
 
122
 
 
123
    // PrinterStopped
 
124
    notifierConnect(QLatin1String("PrinterStopped"),
 
125
                    this,
 
126
                    SIGNAL(printerStopped(QString,QString,QString,uint,QString,bool)));
 
127
 
 
128
    // PrinterShutdown
 
129
    notifierConnect(QLatin1String("PrinterShutdown"),
 
130
                    this,
 
131
                    SIGNAL(printerShutdown(QString,QString,QString,uint,QString,bool)));
 
132
 
 
133
    // PrinterRestarted
 
134
    notifierConnect(QLatin1String("PrinterRestarted"),
 
135
                    this,
 
136
                    SIGNAL(printerRestarted(QString,QString,QString,uint,QString,bool)));
 
137
 
 
138
    // PrinterMediaChanged
 
139
    notifierConnect(QLatin1String("PrinterMediaChanged"),
 
140
                    this,
 
141
                    SIGNAL(printerMediaChanged(QString,QString,QString,uint,QString,bool)));
 
142
 
 
143
    // PrinterFinishingsChanged
 
144
    notifierConnect(QLatin1String("PrinterFinishingsChanged"),
 
145
                    this,
 
146
                    SIGNAL(PrinterFinishingsChanged(QString,QString,QString,uint,QString,bool)));
 
147
 
 
148
    // Job related signals
 
149
    // JobState
 
150
    notifierConnect(QLatin1String("JobState"),
 
151
                    this,
 
152
                    SIGNAL(jobState(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
 
153
 
 
154
    // JobCreated
 
155
    notifierConnect(QLatin1String("JobCreated"),
 
156
                    this,
 
157
                    SIGNAL(jobCreated(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
 
158
 
 
159
    // JobStopped
 
160
    notifierConnect(QLatin1String("JobStopped"),
 
161
                    this,
 
162
                    SIGNAL(jobStopped(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
 
163
 
 
164
    // JobConfigChanged
 
165
    notifierConnect(QLatin1String("JobConfigChanged"),
 
166
                    this,
 
167
                    SIGNAL(jobConfigChanged(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
 
168
 
 
169
    // JobProgress
 
170
    notifierConnect(QLatin1String("JobProgress"),
 
171
                    this,
 
172
                    SIGNAL(jobProgress(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
 
173
 
 
174
    // JobCompleted
 
175
    notifierConnect(QLatin1String("JobCompleted"),
 
176
                    this,
 
177
                    SIGNAL(jobCompleted(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint)));
 
178
 
 
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"),
 
186
                                         this,
 
187
                                         SIGNAL(rhPrinterAdded(QString)));
 
188
 
 
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"),
 
196
                                         this,
 
197
                                         SIGNAL(rhQueueChanged(QString)));
 
198
 
 
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"),
 
206
                                         this,
 
207
                                         SIGNAL(rhPrinterRemoved(QString)));
 
208
 
 
209
    QDBusConnection::systemBus().connect(QLatin1String(""),
 
210
                                         QLatin1String("/com/redhat/PrinterSpooler"),
 
211
                                         QLatin1String("com.redhat.PrinterSpooler"),
 
212
                                         QLatin1String("JobQueuedLocal"),
 
213
                                         this,
 
214
                                         SIGNAL(rhJobQueuedLocal(QString,uint,QString)));
 
215
 
 
216
    QDBusConnection::systemBus().connect(QLatin1String(""),
 
217
                                         QLatin1String("/com/redhat/PrinterSpooler"),
 
218
                                         QLatin1String("com.redhat.PrinterSpooler"),
 
219
                                         QLatin1String("JobStartedLocal"),
 
220
                                         this,
 
221
                                         SIGNAL(rhJobStartedLocal(QString,uint,QString)));
 
222
 
 
223
    // Starts this thread
 
224
    start();
 
225
}
 
226
 
 
227
KCupsConnection::~KCupsConnection()
 
228
{
 
229
    m_instance = 0;
 
230
    if (m_subscriptionId != -1) {
 
231
        cancelDBusSubscription();
 
232
    }
 
233
    m_renewTimer->deleteLater();
 
234
    m_passwordDialog->deleteLater();
 
235
 
 
236
    quit();
 
237
    wait();
 
238
}
 
239
 
 
240
void KCupsConnection::run()
 
241
{
 
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);
 
247
 
 
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);
 
253
 
 
254
    m_inited = true;
 
255
    exec();
 
256
}
 
257
 
 
258
bool KCupsConnection::readyToStart()
 
259
{
 
260
    if (QThread::currentThread() == KCupsConnection::global()) {
 
261
        password_retries = 0;
 
262
        internalErrorCount = 0;
 
263
        return true;
 
264
    }
 
265
    return false;
 
266
}
 
267
 
 
268
ReturnArguments KCupsConnection::request(ipp_op_e operation,
 
269
                                         const char *resource,
 
270
                                         const QVariantHash &reqValues,
 
271
                                         bool needResponse)
 
272
{
 
273
    ReturnArguments ret;
 
274
 
 
275
    if (!readyToStart()) {
 
276
        return ret; // This is not intended to be used in the gui thread
 
277
    }
 
278
 
 
279
    ipp_t *response = NULL;
 
280
    bool needDestName = false;
 
281
    int group_tag = IPP_TAG_PRINTER;
 
282
    do {
 
283
        ipp_t *request;
 
284
        bool isClass = false;
 
285
        QString filename;
 
286
        QVariantHash values = reqValues;
 
287
 
 
288
        ippDelete(response);
 
289
 
 
290
        if (values.contains(QLatin1String("printer-is-class"))) {
 
291
            isClass = values.take(QLatin1String("printer-is-class")).toBool();
 
292
        }
 
293
        if (values.contains(QLatin1String("need-dest-name"))) {
 
294
            needDestName = values.take(QLatin1String("need-dest-name")).toBool();
 
295
        }
 
296
        if (values.contains(QLatin1String("group-tag-qt"))) {
 
297
            group_tag = values.take(QLatin1String("group-tag-qt")).toInt();
 
298
        }
 
299
 
 
300
        if (values.contains(QLatin1String("filename"))) {
 
301
            filename = values.take(QLatin1String("filename")).toString();
 
302
        }
 
303
 
 
304
        // Lets create the request
 
305
        if (values.contains(QLatin1String(KCUPS_PRINTER_NAME))) {
 
306
            request = ippNewDefaultRequest(values.take(QLatin1String(KCUPS_PRINTER_NAME)).toString(),
 
307
                                           isClass,
 
308
                                           operation);
 
309
        } else {
 
310
            request = ippNewRequest(operation);
 
311
        }
 
312
 
 
313
        // send our user name on the request too
 
314
        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
 
315
                     "requesting-user-name", NULL, cupsUser());
 
316
 
 
317
        // Add the requested values to the request
 
318
        requestAddValues(request, values);
 
319
 
 
320
        // Do the request
 
321
        // do the request deleting the response
 
322
        if (filename.isEmpty()) {
 
323
            response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, resource);
 
324
        } else {
 
325
            response = cupsDoFileRequest(CUPS_HTTP_DEFAULT, request, resource, filename.toUtf8());
 
326
        }
 
327
    } while (retry(resource));
 
328
 
 
329
    if (response != NULL && needResponse) {
 
330
        ret = parseIPPVars(response, group_tag, needDestName);
 
331
    }
 
332
    ippDelete(response);
 
333
 
 
334
    return ret;
 
335
}
 
336
 
 
337
int KCupsConnection::createDBusSubscription(const QStringList &events)
 
338
{
 
339
    // Build the current list
 
340
    QStringList currentEvents;
 
341
    foreach (const QStringList &registeredEvents, m_requestedDBusEvents) {
 
342
        currentEvents << registeredEvents;
 
343
    }
 
344
    currentEvents.removeDuplicates();
 
345
 
 
346
    // Check if the requested events are already being asked
 
347
    bool equal = true;
 
348
    foreach (const QString &event, events) {
 
349
        if (!currentEvents.contains(event)) {
 
350
            equal = false;
 
351
            break;
 
352
        }
 
353
    }
 
354
 
 
355
    // Store the subscription
 
356
    int id = 1;
 
357
    if (!m_requestedDBusEvents.isEmpty()) {
 
358
        id = m_requestedDBusEvents.keys().last();
 
359
        ++id;
 
360
    }
 
361
    m_requestedDBusEvents[id] = events;
 
362
 
 
363
    // If the requested list is included in our request just
 
364
    // return an ID
 
365
    if (equal) {
 
366
        return id;
 
367
    }
 
368
 
 
369
    // If we alread have a subscription lets cancel
 
370
    // and create a new one
 
371
    if (m_subscriptionId >= 0) {
 
372
        cancelDBusSubscription();
 
373
    }
 
374
 
 
375
    // Canculates the new events
 
376
    renewDBusSubscription();
 
377
 
 
378
    return id;
 
379
}
 
380
 
 
381
void KCupsConnection::removeDBusSubscription(int subscriptionId)
 
382
{
 
383
    // Collect the list of current events
 
384
    QStringList currentEvents;
 
385
    foreach (const QStringList &registeredEvents, m_requestedDBusEvents) {
 
386
        currentEvents << registeredEvents;
 
387
    }
 
388
    currentEvents.removeDuplicates();
 
389
 
 
390
    QStringList removedEvents = m_requestedDBusEvents.take(subscriptionId);
 
391
 
 
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()) {
 
395
        return;
 
396
    } else {
 
397
        // The requested events changed
 
398
        cancelDBusSubscription();
 
399
 
 
400
        // Canculates the new events
 
401
        renewDBusSubscription();
 
402
    }
 
403
}
 
404
 
 
405
int KCupsConnection::renewDBusSubscription(int subscriptionId, int leaseDuration, const QStringList &events)
 
406
{
 
407
    int ret = -1;
 
408
 
 
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
 
412
    }
 
413
 
 
414
    ipp_t *response = NULL;
 
415
    do {
 
416
        ipp_t *request;
 
417
        ipp_op_e operation;
 
418
 
 
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;
 
423
        } else {
 
424
            operation = IPP_CREATE_PRINTER_SUBSCRIPTION;
 
425
        }
 
426
 
 
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());
 
433
 
 
434
        if (operation == IPP_CREATE_PRINTER_SUBSCRIPTION) {
 
435
            // Add the "notify-events" values to the request
 
436
            QVariantHash values;
 
437
            values["notify-events"] = events;
 
438
            requestAddValues(request, values);
 
439
 
 
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);
 
446
        } else {
 
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);
 
451
        }
 
452
 
 
453
        // Do the request
 
454
        response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
 
455
    } while (retry("/"));
 
456
 
 
457
#ifdef HAVE_CUPS_1_6
 
458
    if (response && ippGetStatusCode(response) == IPP_OK) {
 
459
#else
 
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!";
 
470
            ret = -1;
 
471
        } else {
 
472
#ifdef HAVE_CUPS_1_6
 
473
            ret = ippGetInteger(attr, 0);
 
474
#else
 
475
            ret = attr->values[0].integer;
 
476
#endif // HAVE_CUPS_1_6
 
477
        }
 
478
    } else {
 
479
        kWarning() << "Request failed" << lastError();
 
480
        ret = -1;
 
481
    }
 
482
 
 
483
    ippDelete(response);
 
484
 
 
485
    return ret;
 
486
}
 
487
 
 
488
void KCupsConnection::notifierConnect(const QString &signal, QObject *receiver, const char *slot)
 
489
{
 
490
    QDBusConnection systemBus = QDBusConnection::systemBus();
 
491
    systemBus.connect(QString(),
 
492
                      QLatin1String("/org/cups/cupsd/Notifier"),
 
493
                      QLatin1String("org.cups.cupsd.Notifier"),
 
494
                      signal,
 
495
                      receiver,
 
496
                      slot);
 
497
}
 
498
 
 
499
void KCupsConnection::cancelDBusSubscription()
 
500
{
 
501
    do {
 
502
        ipp_t *request;
 
503
 
 
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);
 
512
 
 
513
        // Do the request
 
514
        ippDelete(cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/"));
 
515
    } while (retry("/"));
 
516
 
 
517
    // Reset the subscription id
 
518
    m_subscriptionId = -1;
 
519
}
 
520
 
 
521
void KCupsConnection::renewDBusSubscription()
 
522
{
 
523
    // check if we have a valid subscription ID
 
524
    kDebug() << m_subscriptionId;
 
525
 
 
526
    if (m_subscriptionId >= 0) {
 
527
        m_subscriptionId = renewDBusSubscription(m_subscriptionId, SUBSCRIPTION_DURATION);
 
528
    }
 
529
 
 
530
    // The above request might fail if the subscription was cancelled
 
531
    if (m_subscriptionId < 0) {
 
532
        QStringList currentEvents;
 
533
        foreach (const QStringList &registeredEvents, m_requestedDBusEvents) {
 
534
            currentEvents << registeredEvents;
 
535
        }
 
536
        currentEvents.removeDuplicates();
 
537
        kDebug() << currentEvents;
 
538
 
 
539
        if (!currentEvents.isEmpty()) {
 
540
            m_subscriptionId = renewDBusSubscription(m_subscriptionId, SUBSCRIPTION_DURATION, currentEvents);
 
541
            m_renewTimer->start();
 
542
        } else {
 
543
            m_renewTimer->stop();
 
544
        }
 
545
    }
 
546
}
 
547
 
 
548
void KCupsConnection::requestAddValues(ipp_t *request, const QVariantHash &values)
 
549
{
 
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()) {
 
555
        case QVariant::Bool:
 
556
            if (key == QLatin1String("printer-is-accepting-jobs")) {
 
557
                ippAddBoolean(request, IPP_TAG_PRINTER,
 
558
                              "printer-is-accepting-jobs", value.toBool());
 
559
            } else {
 
560
                ippAddBoolean(request, IPP_TAG_OPERATION,
 
561
                              key.toUtf8(), value.toBool());
 
562
            }
 
563
            break;
 
564
        case QVariant::Int:
 
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);
 
571
            } else {
 
572
                ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
 
573
                              key.toUtf8(), value.toInt());
 
574
            }
 
575
            break;
 
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());
 
602
            } else {
 
603
                ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
 
604
                             key.toUtf8(), "utf-8", value.toString().toUtf8());
 
605
            }
 
606
            break;
 
607
        case QVariant::StringList:
 
608
            {
 
609
                QStringList list = value.value<QStringList>();
 
610
 
 
611
                QList<QByteArray> valuesQByteArrayList;
 
612
                const char **values = qStringListToCharPtrPtr(list, &valuesQByteArrayList);
 
613
 
 
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);
 
625
                } else {
 
626
                    ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
 
627
                                  i.key().toUtf8(), list.size(), "utf-8", values);
 
628
                }
 
629
 
 
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.
 
632
                delete [] values;
 
633
            }
 
634
            break;
 
635
        case QVariant::UInt:
 
636
            ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
 
637
                          key.toUtf8(), value.toInt());
 
638
            break;
 
639
        default:
 
640
            kWarning() << "type NOT recognized! This will be ignored:" << key << "values" << value;
 
641
        }
 
642
        ++i;
 
643
    }
 
644
}
 
645
 
 
646
ReturnArguments KCupsConnection::parseIPPVars(ipp_t *response, int group_tag, bool needDestName)
 
647
{
 
648
    ipp_attribute_t *attr;
 
649
    ReturnArguments ret;
 
650
 
 
651
#ifdef HAVE_CUPS_1_6
 
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();
 
658
            continue;
 
659
        }
 
660
 
 
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)) {
 
674
            continue;
 
675
        }
 
676
 
 
677
        // Add a printer description attribute...
 
678
        destAttributes[QString::fromUtf8(ippGetName(attr))] = ippAttrToVariant(attr);
 
679
    }
 
680
 
 
681
    if (!destAttributes.isEmpty()) {
 
682
        ret << destAttributes;
 
683
    }
 
684
#else
 
685
    for (attr = response->attrs; attr != NULL; attr = attr->next) {
 
686
       /*
 
687
        * Skip leading attributes until we hit a a group which can be a printer, job...
 
688
        */
 
689
        while (attr && attr->group_tag != group_tag) {
 
690
            attr = attr->next;
 
691
        }
 
692
 
 
693
        if (attr == NULL) {
 
694
            break;
 
695
        }
 
696
 
 
697
        /*
 
698
         * Pull the needed attributes from this printer...
 
699
         */
 
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) {
 
713
                continue;
 
714
            }
 
715
 
 
716
            /*
 
717
             * Add a printer description attribute...
 
718
             */
 
719
            destAttributes[QString::fromUtf8(attr->name)] = ippAttrToVariant(attr);
 
720
        }
 
721
 
 
722
        /*
 
723
         * See if we have everything needed...
 
724
         */
 
725
        if (needDestName && destAttributes[QLatin1String(KCUPS_PRINTER_NAME)].toString().isEmpty()) {
 
726
            if (attr == NULL) {
 
727
                break;
 
728
            } else {
 
729
                continue;
 
730
            }
 
731
        }
 
732
 
 
733
        ret << destAttributes;
 
734
 
 
735
        if (attr == NULL) {
 
736
            break;
 
737
        }
 
738
    }
 
739
#endif // HAVE_CUPS_1_6
 
740
 
 
741
    return ret;
 
742
}
 
743
 
 
744
// Don't forget to delete the request
 
745
ipp_t* KCupsConnection::ippNewDefaultRequest(const QString &name, bool isClass, ipp_op_t operation)
 
746
{
 
747
    char  uri[HTTP_MAX_URI]; // printer URI
 
748
    ipp_t *request;
 
749
 
 
750
    QString destination;
 
751
    if (isClass) {
 
752
        destination = QLatin1String("/classes/") % name;
 
753
    } else {
 
754
        destination = QLatin1String("/printers/") % name;
 
755
    }
 
756
 
 
757
    // Create a new request
 
758
    // where we need:
 
759
    // * printer-uri
 
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,
 
765
                 "utf-8", uri);
 
766
 
 
767
    return request;
 
768
}
 
769
 
 
770
QVariant KCupsConnection::ippAttrToVariant(ipp_attribute_t *attr)
 
771
{
 
772
    QVariant ret;
 
773
#ifdef HAVE_CUPS_1_6
 
774
    switch (ippGetValueTag(attr)) {
 
775
    case IPP_TAG_INTEGER:
 
776
    case IPP_TAG_ENUM:
 
777
        if (ippGetCount(attr) == 1) {
 
778
            ret = ippGetInteger(attr, 0);
 
779
        } else {
 
780
            QList<int> values;
 
781
            for (int i = 0; i < ippGetCount(attr); ++i) {
 
782
                values << ippGetInteger(attr, i);
 
783
            }
 
784
            ret = qVariantFromValue(values);
 
785
        }
 
786
        break;
 
787
    case IPP_TAG_BOOLEAN:
 
788
        if (ippGetCount(attr)== 1) {
 
789
            ret = ippGetBoolean(attr, 0);
 
790
        } else {
 
791
            QList<bool> values;
 
792
            for (int i = 0; i < ippGetCount(attr); ++i) {
 
793
                values << ippGetBoolean(attr, i);
 
794
            }
 
795
            ret = qVariantFromValue(values);
 
796
        }
 
797
        break;
 
798
    case IPP_TAG_RANGE:
 
799
    {
 
800
        QVariantList values;
 
801
        for (int i = 0; i < ippGetCount(attr); ++i) {
 
802
            int rangeUpper;
 
803
            values << ippGetRange(attr, i, &rangeUpper);
 
804
            values << rangeUpper;
 
805
        }
 
806
        ret = values;
 
807
    }
 
808
        break;
 
809
    default:
 
810
        if (ippGetCount(attr)== 1) {
 
811
            ret = QString::fromUtf8(ippGetString(attr, 0, NULL));
 
812
        } else {
 
813
            QStringList values;
 
814
            for (int i = 0; i < ippGetCount(attr); ++i) {
 
815
                values << QString::fromUtf8(ippGetString(attr, i, NULL));
 
816
            }
 
817
            ret = values;
 
818
        }
 
819
    }
 
820
#else
 
821
    switch (attr->value_tag) {
 
822
    case IPP_TAG_INTEGER:
 
823
    case IPP_TAG_ENUM:
 
824
        if (attr->num_values == 1) {
 
825
            ret = attr->values[0].integer;
 
826
        } else {
 
827
            QList<int> values;
 
828
            for (int i = 0; i < attr->num_values; ++i) {
 
829
                values << attr->values[i].integer;
 
830
            }
 
831
            ret = qVariantFromValue(values);
 
832
        }
 
833
        break;
 
834
    case IPP_TAG_BOOLEAN:
 
835
        if (attr->num_values == 1) {
 
836
            ret = static_cast<bool>(attr->values[0].integer);
 
837
        } else {
 
838
            QList<bool> values;
 
839
            for (int i = 0; i < attr->num_values; ++i) {
 
840
                values << static_cast<bool>(attr->values[i].integer);
 
841
            }
 
842
            ret = qVariantFromValue(values);
 
843
        }
 
844
        break;
 
845
    case IPP_TAG_RANGE:
 
846
    {
 
847
        QVariantList 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;
 
851
        }
 
852
        ret = values;
 
853
    }
 
854
        break;
 
855
    default:
 
856
        if (attr->num_values == 1) {
 
857
            ret = QString::fromUtf8(attr->values[0].string.text);
 
858
        } else {
 
859
            QStringList values;
 
860
            for (int i = 0; i < attr->num_values; ++i) {
 
861
                values << QString::fromUtf8(attr->values[i].string.text);
 
862
            }
 
863
            ret = values;
 
864
        }
 
865
    }
 
866
#endif // HAVE_CUPS_1_6
 
867
    return ret;
 
868
}
 
869
 
 
870
bool KCupsConnection::retry(const char *resource)
 
871
{
 
872
    ipp_status_t status = cupsLastError();
 
873
 
 
874
    kDebug() << "cupsLastError():" << status << cupsLastErrorString();
 
875
 
 
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);
 
883
 
 
884
        // Server might be restarting sleep for a few ms
 
885
        msleep(500);
 
886
 
 
887
        // Try the request again
 
888
        return ++internalErrorCount < 3;
 
889
    }
 
890
 
 
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
 
899
        cupsSetUser("root");
 
900
 
 
901
        // force authentication
 
902
        forceAuth = true;
 
903
    }
 
904
 
 
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;
 
912
            return false;
 
913
        }
 
914
 
 
915
        // force authentication
 
916
        forceAuth = true;
 
917
    }
 
918
 
 
919
    if (forceAuth) {
 
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);
 
924
 
 
925
        // If the authentication was succefull
 
926
        // sometimes just trying to be root works
 
927
        return ret == -1 ? true : false;
 
928
    }
 
929
 
 
930
    // the action was not forbidden
 
931
    return false;
 
932
}
 
933
 
 
934
ipp_status_t KCupsConnection::lastError()
 
935
{
 
936
    ipp_status_t status = cupsLastError();
 
937
 
 
938
    return status;
 
939
}
 
940
 
 
941
const char * password_cb(const char *prompt, http_t *http, const char *method, const char *resource, void *user_data)
 
942
{
 
943
    Q_UNUSED(prompt)
 
944
    Q_UNUSED(http)
 
945
    Q_UNUSED(method)
 
946
    Q_UNUSED(resource)
 
947
 
 
948
    if (++password_retries > 3) {
 
949
        // cancel the authentication
 
950
        cupsSetUser(NULL);
 
951
        return NULL;
 
952
    }
 
953
 
 
954
    KCupsPasswordDialog *passwordDialog = static_cast<KCupsPasswordDialog *>(user_data);
 
955
    bool wrongPassword = password_retries > 1;
 
956
 
 
957
    // This will block this thread until exec is not finished
 
958
    kDebug() << password_retries;
 
959
    QMetaObject::invokeMethod(passwordDialog,
 
960
                              "exec",
 
961
                              Qt::BlockingQueuedConnection,
 
962
                              Q_ARG(QString, QString::fromUtf8(cupsUser())),
 
963
                              Q_ARG(bool, wrongPassword));
 
964
    kDebug() << passwordDialog->accepted();
 
965
 
 
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();
 
971
    } else {
 
972
        // the dialog was canceled
 
973
        password_retries = -1;
 
974
        cupsSetUser(NULL);
 
975
        return NULL;
 
976
    }
 
977
}