1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2014 John Layt <jlayt@kde.org>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qcupsprintersupport_p.h"
6
7#include "qcupsprintengine_p.h"
8#include "qppdprintdevice.h"
9#include <private/qprinterinfo_p.h>
10#include <private/qprintdevice_p.h>
11
12#include <QtPrintSupport/QPrinterInfo>
13
14#if QT_CONFIG(cupspassworddialog)
15#include <QGuiApplication>
16#include <QDialog>
17#include <QDialogButtonBox>
18#include <QFormLayout>
19#include <QLabel>
20#include <QLineEdit>
21#endif // QT_CONFIG(cupspassworddialog)
22
23#include <cups/ppd.h>
24#ifndef QT_LINUXBASE // LSB merges everything into cups.h
25# include <cups/language.h>
26#endif
27
28QT_BEGIN_NAMESPACE
29
30#if QT_CONFIG(cupspassworddialog)
31static const char *getPasswordCB(const char */*prompt*/, http_t *http, const char */*method*/, const char *resource, void */*user_data*/)
32{
33 // cups doesn't free the const char * we return so keep around
34 // the last password so we don't leak memory if called multiple times.
35 static QByteArray password;
36
37 // prompt is always "Password for %s on %s? " but we can't use it since we allow the user to change the user.
38 // That is fine because cups always calls cupsUser after calling this callback.
39 // We build our own prompt with the hostname (if not localhost) and the resource that is being used
40
41 char hostname[HTTP_MAX_HOST];
42 httpGetHostname(http, s: hostname, HTTP_MAX_HOST);
43
44 const QString username = QString::fromLocal8Bit(ba: cupsUser());
45
46 QDialog dialog;
47 dialog.setWindowTitle(QCoreApplication::translate(context: "QCupsPrinterSupport", key: "Authentication Needed"));
48
49 QFormLayout *layout = new QFormLayout(&dialog);
50 layout->setSizeConstraint(QLayout::SetFixedSize);
51
52 QLineEdit *usernameLE = new QLineEdit();
53 usernameLE->setText(username);
54
55 QLineEdit *passwordLE = new QLineEdit();
56 passwordLE->setEchoMode(QLineEdit::Password);
57
58 QString resourceString = QString::fromLocal8Bit(ba: resource);
59 if (resourceString.startsWith(QStringLiteral("/printers/")))
60 resourceString = resourceString.mid(QStringLiteral("/printers/").size());
61
62 QLabel *label = new QLabel();
63 if (hostname == QStringLiteral("localhost")) {
64 label->setText(QCoreApplication::translate(context: "QCupsPrinterSupport", key: "Authentication needed to use %1.").arg(a: resourceString));
65 } else {
66 label->setText(QCoreApplication::translate(context: "QCupsPrinterSupport", key: "Authentication needed to use %1 on %2.").arg(a: resourceString).arg(a: hostname));
67 label->setWordWrap(true);
68 }
69
70 layout->addRow(widget: label);
71 layout->addRow(label: new QLabel(QCoreApplication::translate(context: "QCupsPrinterSupport", key: "Username:")), field: usernameLE);
72 layout->addRow(label: new QLabel(QCoreApplication::translate(context: "QCupsPrinterSupport", key: "Password:")), field: passwordLE);
73
74 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
75 layout->addRow(widget: buttonBox);
76
77 QObject::connect(sender: buttonBox, signal: &QDialogButtonBox::accepted, context: &dialog, slot: &QDialog::accept);
78 QObject::connect(sender: buttonBox, signal: &QDialogButtonBox::rejected, context: &dialog, slot: &QDialog::reject);
79
80 passwordLE->setFocus();
81
82 if (dialog.exec() != QDialog::Accepted)
83 return nullptr;
84
85 if (usernameLE->text() != username)
86 cupsSetUser(user: usernameLE->text().toLocal8Bit().constData());
87
88 password = passwordLE->text().toLocal8Bit();
89
90 return password.constData();
91}
92#endif // QT_CONFIG(cupspassworddialog)
93
94QCupsPrinterSupport::QCupsPrinterSupport()
95 : QPlatformPrinterSupport()
96{
97#if QT_CONFIG(cupspassworddialog)
98 // Only show password dialog if GUI application
99 if (qobject_cast<QGuiApplication*>(object: QCoreApplication::instance()))
100 cupsSetPasswordCB2(cb: getPasswordCB, user_data: nullptr /* user_data */ );
101#endif // QT_CONFIG(cupspassworddialog)
102}
103
104QCupsPrinterSupport::~QCupsPrinterSupport()
105{
106}
107
108QPrintEngine *QCupsPrinterSupport::createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId)
109{
110 return new QCupsPrintEngine(printerMode, (deviceId.isEmpty() ? defaultPrintDeviceId() : deviceId));
111}
112
113QPaintEngine *QCupsPrinterSupport::createPaintEngine(QPrintEngine *engine, QPrinter::PrinterMode printerMode)
114{
115 Q_UNUSED(printerMode);
116 return static_cast<QCupsPrintEngine *>(engine);
117}
118
119QPrintDevice QCupsPrinterSupport::createPrintDevice(const QString &id)
120{
121 return QPlatformPrinterSupport::createPrintDevice(device: new QPpdPrintDevice(id));
122}
123
124QStringList QCupsPrinterSupport::availablePrintDeviceIds() const
125{
126 QStringList list;
127 cups_dest_t *dests;
128 int count = cupsGetDests(dests: &dests);
129 list.reserve(asize: count);
130 for (int i = 0; i < count; ++i) {
131 QString printerId = QString::fromLocal8Bit(ba: dests[i].name);
132 if (dests[i].instance)
133 printerId += u'/' + QString::fromLocal8Bit(ba: dests[i].instance);
134 list.append(t: printerId);
135 }
136 cupsFreeDests(num_dests: count, dests);
137 return list;
138}
139
140QString QCupsPrinterSupport::defaultPrintDeviceId() const
141{
142 return staticDefaultPrintDeviceId();
143}
144
145QString QCupsPrinterSupport::staticDefaultPrintDeviceId()
146{
147 QString printerId;
148 cups_dest_t *dests;
149 int count = cupsGetDests(dests: &dests);
150 for (int i = 0; i < count; ++i) {
151 if (dests[i].is_default) {
152 printerId = QString::fromLocal8Bit(ba: dests[i].name);
153 if (dests[i].instance) {
154 printerId += u'/' + QString::fromLocal8Bit(ba: dests[i].instance);
155 break;
156 }
157 }
158 }
159 cupsFreeDests(num_dests: count, dests);
160 return printerId;
161}
162
163QT_END_NAMESPACE
164

source code of qtbase/src/plugins/printsupport/cups/qcupsprintersupport.cpp