Merge lp:~jonas-drange/ubuntu-ui-extras/fix-ipp-client-build-failure-on-zesty into lp:ubuntu-ui-extras

Proposed by Jonas G. Drange
Status: Superseded
Proposed branch: lp:~jonas-drange/ubuntu-ui-extras/fix-ipp-client-build-failure-on-zesty
Merge into: lp:ubuntu-ui-extras
Diff against target: 12308 lines (+11922/-4)
61 files modified
debian/control (+3/-0)
modules/Ubuntu/Components/Extras/CMakeLists.txt (+1/-0)
modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml (+136/-0)
modules/Ubuntu/Components/Extras/Example/Printers.qml (+730/-0)
modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt (+70/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp (+297/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend.h (+208/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp (+795/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h (+149/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp (+127/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.h (+47/-0)
modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp (+70/-0)
modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h (+57/-0)
modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp (+1114/-0)
modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h (+130/-0)
modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.cpp (+127/-0)
modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.h (+61/-0)
modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp (+53/-0)
modules/Ubuntu/Components/Extras/Printers/cups/printerloader.h (+49/-0)
modules/Ubuntu/Components/Extras/Printers/enums.h (+129/-0)
modules/Ubuntu/Components/Extras/Printers/i18n.cpp (+42/-0)
modules/Ubuntu/Components/Extras/Printers/i18n.h (+29/-0)
modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp (+149/-0)
modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h (+78/-0)
modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp (+175/-0)
modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h (+82/-0)
modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp (+454/-0)
modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h (+141/-0)
modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp (+549/-0)
modules/Ubuntu/Components/Extras/Printers/models/printermodel.h (+163/-0)
modules/Ubuntu/Components/Extras/Printers/org.cups.cupsd.Notifier.xml (+146/-0)
modules/Ubuntu/Components/Extras/Printers/plugin.cpp (+59/-0)
modules/Ubuntu/Components/Extras/Printers/plugin.h (+33/-0)
modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp (+401/-0)
modules/Ubuntu/Components/Extras/Printers/printer/printer.h (+118/-0)
modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp (+510/-0)
modules/Ubuntu/Components/Extras/Printers/printer/printerjob.h (+172/-0)
modules/Ubuntu/Components/Extras/Printers/printer/printersignalhandler.cpp (+69/-0)
modules/Ubuntu/Components/Extras/Printers/printer/printersignalhandler.h (+55/-0)
modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp (+335/-0)
modules/Ubuntu/Components/Extras/Printers/printers/printers.h (+123/-0)
modules/Ubuntu/Components/Extras/Printers/printers_global.h (+23/-0)
modules/Ubuntu/Components/Extras/Printers/qmldir (+2/-0)
modules/Ubuntu/Components/Extras/Printers/structs.h (+174/-0)
modules/Ubuntu/Components/Extras/Printers/utils.h (+110/-0)
po/CMakeLists.txt (+2/-1)
po/ubuntu-ui-extras.pot (+51/-3)
tests/unittests/CMakeLists.txt (+2/-0)
tests/unittests/Printers/CMakeLists.txt (+58/-0)
tests/unittests/Printers/mockbackend.h (+448/-0)
tests/unittests/Printers/tst_drivermodel.cpp (+136/-0)
tests/unittests/Printers/tst_jobfilter.cpp (+55/-0)
tests/unittests/Printers/tst_jobmodel.cpp (+511/-0)
tests/unittests/Printers/tst_printer.cpp (+343/-0)
tests/unittests/Printers/tst_printerdevice.cpp (+131/-0)
tests/unittests/Printers/tst_printerdevicemodel.cpp (+162/-0)
tests/unittests/Printers/tst_printerfilter.cpp (+156/-0)
tests/unittests/Printers/tst_printerjob.cpp (+318/-0)
tests/unittests/Printers/tst_printermodel.cpp (+635/-0)
tests/unittests/Printers/tst_printers.cpp (+325/-0)
tests/unittests/Printers/tst_signalhandler.cpp (+44/-0)
To merge this branch: bzr merge lp:~jonas-drange/ubuntu-ui-extras/fix-ipp-client-build-failure-on-zesty
Reviewer Review Type Date Requested Status
Ubuntu Phablet Team Pending
Review via email: mp+319709@code.launchpad.net

Commit message

includes cups/adminutils.h into which the cups device callback was moved in >= libcups2-dev 2.2.2

Description of the change

includes cups/adminutils.h into which the cups device callback was moved in >= libcups2-dev 2.2.2

To post a comment you must log in.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2017-02-22 10:51:22 +0000
3+++ debian/control 2017-03-13 15:55:01 +0000
4@@ -3,8 +3,10 @@
5 Priority: optional
6 Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
7 Build-Depends: cmake (>= 2.8.9),
8+ cmake-extras (>= 0.10),
9 debhelper (>= 9),
10 gettext,
11+ libcups2-dev,
12 libexiv2-dev,
13 pkg-config,
14 python:any,
15@@ -30,6 +32,7 @@
16 Architecture: any
17 Depends: ${misc:Depends},
18 ${shlibs:Depends},
19+ libqt5printsupport5,
20 qml-module-qtqml-models2,
21 qml-module-qtquick2,
22 qml-module-ubuntu-components,
23
24=== modified file 'modules/Ubuntu/Components/Extras/CMakeLists.txt'
25--- modules/Ubuntu/Components/Extras/CMakeLists.txt 2016-12-08 14:26:41 +0000
26+++ modules/Ubuntu/Components/Extras/CMakeLists.txt 2017-03-13 15:55:01 +0000
27@@ -13,4 +13,5 @@
28 add_subdirectory(plugin)
29 add_subdirectory(Example)
30 add_subdirectory(PhotoEditor)
31+add_subdirectory(Printers)
32 add_subdirectory(TabsBar)
33
34=== added file 'modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml'
35--- modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml 1970-01-01 00:00:00 +0000
36+++ modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml 2017-03-13 15:55:01 +0000
37@@ -0,0 +1,136 @@
38+/*
39+ * Copyright 2017 Canonical Ltd.
40+ *
41+ * This program is free software; you can redistribute it and/or modify
42+ * it under the terms of the GNU Lesser General Public License as published by
43+ * the Free Software Foundation; version 3.
44+ *
45+ * This program is distributed in the hope that it will be useful,
46+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
47+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48+ * GNU Lesser General Public License for more details.
49+ *
50+ * You should have received a copy of the GNU Lesser General Public License
51+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
52+ *
53+ * Authored by Jonas G. Drange <jonas.drange@canonical.com>
54+ * Andrew Hayzen <andrew.hayzen@canonical.com>
55+ */
56+
57+import QtQuick 2.4
58+import QtQuick.Layouts 1.1
59+import Ubuntu.Components 1.3
60+import Ubuntu.Components.ListItems 1.3 as ListItems
61+import Ubuntu.Components.Extras.Printers 0.1
62+
63+MainView {
64+ width: units.gu(50)
65+ height: units.gu(90)
66+
67+ Component {
68+ id: queuePage
69+
70+ Page {
71+ header: PageHeader {
72+ title: "Queue: " + printer.name
73+ flickable: queueView
74+ }
75+ visible: false
76+
77+ property var printer
78+
79+ ListView {
80+ id: queueView
81+ anchors {
82+ fill: parent
83+ }
84+ delegate: ListItem {
85+ height: modelLayout.height + (divider.visible ? divider.height : 0)
86+ trailingActions: ListItemActions {
87+ actions: [
88+ Action {
89+ iconName: model.held ? "media-playback-start" : "media-playback-pause"
90+ text: model.held ? "Release" : "Hold"
91+
92+ onTriggered: {
93+ if (model.held) {
94+ Printers.releaseJob(printer.name, model.id);
95+ } else {
96+ Printers.holdJob(printer.name, model.id);
97+ }
98+ }
99+ },
100+ Action {
101+ iconName: "cancel"
102+ text: "Cancel"
103+
104+ onTriggered: Printers.cancelJob(printer.name, model.id);
105+ }
106+ ]
107+ }
108+
109+ ListItemLayout {
110+ id: modelLayout
111+ title.text: displayName
112+ subtitle.text: model.title + " (" + model.id + ") State: " + model.state
113+ + " Color: " + model.colorModel + " CreationTime: "
114+ + model.creationTime + " PageRange: "
115+ + model.printRange + " Messages: " + model.messages;
116+ subtitle.wrapMode: Text.WrapAtWordBoundaryOrAnywhere
117+ subtitle.maximumLineCount: 3
118+ }
119+ }
120+ model: printer.jobs
121+
122+ Label {
123+ anchors {
124+ centerIn: parent
125+ }
126+ text: "Empty queue"
127+ visible: queueView.count === 0
128+ }
129+ }
130+ }
131+ }
132+
133+ PageStack {
134+ id: pageStack
135+
136+ Page {
137+ id: printersPage
138+ header: PageHeader {
139+ title: "Printers"
140+ flickable: printerList
141+ }
142+ visible: false
143+
144+ ListView {
145+ id: printerList
146+ anchors { fill: parent }
147+ model: Printers.allPrintersWithPdf
148+ delegate: ListItem {
149+ height: modelLayout.height + (divider.visible ? divider.height : 0)
150+ ListItemLayout {
151+ id: modelLayout
152+ title.text: displayName
153+ title.font.bold: model.default
154+ subtitle.text: description
155+
156+ Icon {
157+ id: icon
158+ width: height
159+ height: units.gu(2.5)
160+ name: "printer-symbolic"
161+ SlotsLayout.position: SlotsLayout.First
162+ }
163+
164+ ProgressionSlot {}
165+ }
166+ onClicked: pageStack.push(queuePage, { printer: model })
167+ }
168+ }
169+ }
170+
171+ Component.onCompleted: push(printersPage)
172+ }
173+}
174
175=== added file 'modules/Ubuntu/Components/Extras/Example/Printers.qml'
176--- modules/Ubuntu/Components/Extras/Example/Printers.qml 1970-01-01 00:00:00 +0000
177+++ modules/Ubuntu/Components/Extras/Example/Printers.qml 2017-03-13 15:55:01 +0000
178@@ -0,0 +1,730 @@
179+/*
180+ * Copyright 2017 Canonical Ltd.
181+ *
182+ * This program is free software; you can redistribute it and/or modify
183+ * it under the terms of the GNU Lesser General Public License as published by
184+ * the Free Software Foundation; version 3.
185+ *
186+ * This program is distributed in the hope that it will be useful,
187+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
188+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
189+ * GNU Lesser General Public License for more details.
190+ *
191+ * You should have received a copy of the GNU Lesser General Public License
192+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
193+ *
194+ * Authored by Jonas G. Drange <jonas.drange@canonical.com>
195+ */
196+
197+import QtQuick 2.4
198+import QtQuick.Layouts 1.1
199+import Ubuntu.Components 1.3
200+import Ubuntu.Components.Popups 1.3
201+import Ubuntu.Components.ListItems 1.3 as ListItems
202+import Ubuntu.Components.Extras.Printers 0.1
203+
204+MainView {
205+ width: units.gu(50)
206+ height: units.gu(90)
207+
208+ Component {
209+ id: printerPage
210+
211+ Page {
212+ visible: false
213+ property var printer
214+ header: PageHeader {
215+ id: printerPageHeader
216+ title: printer.name
217+ flickable: printerFlickable
218+ }
219+
220+ Component {
221+ id: printerPageNotYetLoaded
222+
223+ Item {
224+ anchors.fill: parent
225+ ActivityIndicator {
226+ anchors.centerIn: parent
227+ running: true
228+ }
229+ }
230+ }
231+
232+ Flickable {
233+ id: printerFlickable
234+ anchors.fill: parent
235+ contentHeight: contentItem.childrenRect.height
236+ Loader {
237+ id: printerPageBitsLoader
238+ anchors {
239+ left: parent.left
240+ right: parent.right
241+ }
242+
243+ sourceComponent: printer.isLoaded ? printerPageLoaded : printerPageNotYetLoaded
244+ }
245+ }
246+
247+ Component {
248+ id: printerPageLoaded
249+
250+ Column {
251+ height: childrenRect.height + anchors.topMargin
252+ anchors {
253+ top: parent.top
254+ topMargin: units.gu(2)
255+ left: parent.left
256+ right: parent.right
257+ }
258+
259+ ListItems.Standard {
260+ anchors {
261+ left: parent.left
262+ right: parent.right
263+ }
264+ text: "Enabled"
265+
266+ control: Switch {
267+ checked: printer.printerEnabled
268+ onCheckedChanged: printer.printerEnabled = checked
269+ }
270+ }
271+
272+ ListItems.SingleValue {
273+ anchors {
274+ left: parent.left
275+ right: parent.right
276+ }
277+ text: "Status"
278+
279+ value: {
280+ var state;
281+ if (printer.state == PrinterEnum.IdleState) {
282+ state = i18n.tr("Idle");
283+ } else if (printer.state == PrinterEnum.AbortedState) {
284+ state = i18n.tr("Aborted");
285+ } else if (printer.state == PrinterEnum.ActiveState) {
286+ state = i18n.tr("Active");
287+ } else if (printer.state == PrinterEnum.ErrorState) {
288+ state = i18n.tr("Stopped");
289+ }
290+ return "%1 — %2"
291+ .arg(state)
292+ .arg(printer.lastMessage ?
293+ printer.lastMessage : i18n.tr("No messages"));
294+ }
295+ }
296+
297+ ListItems.Standard {
298+ anchors {
299+ left: parent.left
300+ right: parent.right
301+ }
302+ text: "Accepting jobs"
303+
304+ control: Switch {
305+ checked: printer.acceptJobs
306+ onCheckedChanged: printer.acceptJobs = checked
307+ }
308+ }
309+
310+ ListItems.Standard {
311+ anchors {
312+ left: parent.left
313+ right: parent.right
314+ }
315+ text: "Shared"
316+
317+ control: Switch {
318+ checked: printer.shared
319+ onCheckedChanged: printer.shared = checked
320+ }
321+ }
322+
323+ ListItems.Standard {
324+ anchors {
325+ left: parent.left
326+ right: parent.right
327+ }
328+ text: "Jobs"
329+ progression: true
330+ onClicked: pageStack.push(jobPage, { printer: printer })
331+ }
332+
333+ ListItems.Standard {
334+ anchors {
335+ left: parent.left
336+ right: parent.right
337+ }
338+ text: "Description"
339+
340+ control: TextField {
341+ anchors {
342+ margins: units.gu(1)
343+ left: parent.left
344+ right: parent.right
345+
346+ }
347+ text: printer.description
348+ onTextChanged: printer.description = text
349+ }
350+ }
351+
352+
353+ ListItems.ValueSelector {
354+ anchors {
355+ left: parent.left
356+ right: parent.right
357+ }
358+ enabled: values.length > 1
359+ text: "Duplex"
360+ values: printer.supportedDuplexModes
361+ onSelectedIndexChanged: printer.duplexMode = selectedIndex
362+ Component.onCompleted: {
363+ if (enabled) {
364+ selectedIndex = printer.duplexMode
365+ }
366+ }
367+ }
368+
369+ ListItems.ValueSelector {
370+ anchors {
371+ left: parent.left
372+ right: parent.right
373+ }
374+ text: "Page size"
375+ values: printer.supportedPageSizes
376+ onSelectedIndexChanged: printer.pageSize = selectedIndex
377+ Component.onCompleted: selectedIndex = printer.supportedPageSizes.indexOf(printer.pageSize)
378+ }
379+
380+ ListItems.ValueSelector {
381+ anchors {
382+ left: parent.left
383+ right: parent.right
384+ }
385+ visible: printer.supportedColorModels.length
386+ text: "Color model"
387+ values: printer.supportedColorModels
388+ enabled: values.length > 1
389+ onSelectedIndexChanged: printer.colorModel = selectedIndex
390+ Component.onCompleted: {
391+ if (enabled)
392+ selectedIndex = printer.colorModel
393+ }
394+ }
395+
396+ ListItems.ValueSelector {
397+ anchors {
398+ left: parent.left
399+ right: parent.right
400+ }
401+ visible: printer.supportedPrintQualities.length
402+ text: "Quality"
403+ values: printer.supportedPrintQualities
404+ enabled: values.length > 1
405+ onSelectedIndexChanged: printer.printQuality = selectedIndex
406+ Component.onCompleted: {
407+ if (enabled)
408+ selectedIndex = printer.printQuality
409+ }
410+ }
411+
412+ ListItems.SingleControl {
413+ anchors {
414+ left: parent.left
415+ right: parent.right
416+ }
417+ control: Button {
418+ text: "Print test page"
419+ onClicked: Printers.printTestPage(printer.name)
420+ }
421+ }
422+
423+ ListItems.SingleValue {
424+ anchors {
425+ left: parent.left
426+ right: parent.right
427+ }
428+ text: "Device URI"
429+ value: printer.deviceUri
430+ }
431+ }
432+ }
433+ }
434+ }
435+
436+ Component {
437+ id: jobPage
438+ Page {
439+ property var printer
440+ header: PageHeader {
441+ id: jobPageHeader
442+ title: "%1 (%2 jobs)".arg(printer.name).arg(jobList.count)
443+ flickable: jobList
444+ }
445+
446+ ListView {
447+ id: jobList
448+ anchors.fill: parent
449+ model: printer.jobs
450+ delegate: ListItem {
451+ height: jobLayout.height + (divider.visible ? divider.height : 0)
452+ ListItemLayout {
453+ id: jobLayout
454+ title.text: displayName
455+
456+ Icon {
457+ id: icon
458+ width: height
459+ height: units.gu(2.5)
460+ name: "stock_document"
461+ SlotsLayout.position: SlotsLayout.First
462+ }
463+ }
464+ }
465+ }
466+ }
467+ }
468+
469+
470+ Component {
471+ id: allJobsPage
472+ Page {
473+ header: PageHeader {
474+ id: allJobsHeader
475+ title: "Printer jobs"
476+ flickable: jobsList
477+ }
478+
479+ ListView {
480+ id: jobsList
481+ anchors.fill: parent
482+ model: Printers.printJobs
483+ delegate: ListItem {
484+ height: jobsLayout.height + (divider.visible ? divider.height : 0)
485+ ListItemLayout {
486+ id: jobsLayout
487+ title.text: displayName
488+
489+ Icon {
490+ id: icon
491+ width: height
492+ height: units.gu(2.5)
493+ name: "stock_document"
494+ SlotsLayout.position: SlotsLayout.First
495+ }
496+ }
497+ }
498+ }
499+ }
500+ }
501+
502+
503+ PageStack {
504+ id: pageStack
505+
506+ Component.onCompleted: push(printersPage)
507+
508+ Page {
509+ id: printersPage
510+ header: PageHeader {
511+ title: "Printers"
512+ flickable: printerList
513+ trailingActionBar {
514+ actions: [
515+ Action {
516+ iconName: "add"
517+ text: "Add printer"
518+ onTriggered: pageStack.push(addPrinterPageComponent)
519+ },
520+ Action {
521+ iconName: "document-print"
522+ text: "Printer jobs"
523+ onTriggered: pageStack.push(allJobsPage)
524+ }
525+ ]
526+ }
527+ }
528+ visible: false
529+
530+ ListView {
531+ id: printerList
532+ anchors { fill: parent }
533+ model: Printers.allPrintersWithPdf
534+ delegate: ListItem {
535+ height: modelLayout.height + (divider.visible ? divider.height : 0)
536+ trailingActions: ListItemActions {
537+ actions: [
538+ Action {
539+ iconName: "delete"
540+ onTriggered: {
541+ if (!Printers.removePrinter(model.name)) {
542+ console.error('failed to remove printer', Printers.lastMessage);
543+ }
544+ }
545+ },
546+ Action {
547+ iconName: model.default ? "starred" : "non-starred"
548+ enabled: !model.default
549+ onTriggered: Printers.defaultPrinterName = model.name
550+ }
551+
552+ ]
553+ }
554+ ListItemLayout {
555+ id: modelLayout
556+ title.text: displayName
557+ title.font.bold: model.default
558+
559+ Icon {
560+ id: icon
561+ width: height
562+ height: units.gu(2.5)
563+ name: "printer-symbolic"
564+ SlotsLayout.position: SlotsLayout.First
565+ }
566+
567+ ProgressionSlot {}
568+ }
569+ onClicked: {
570+ Printers.loadPrinter(model.name);
571+ pageStack.push(printerPage, { printer: model });
572+ }
573+ }
574+ }
575+
576+ Label {
577+ anchors.centerIn: parent
578+ visible: printerList.count === 0
579+ text: "No printers found"
580+ }
581+ }
582+ }
583+
584+ Component {
585+ id: addPrinterPageComponent
586+ Page {
587+ id: addPrinterPage
588+ states: [
589+ State {
590+ name: "success"
591+ PropertyChanges {
592+ target: okAction
593+ enabled: false
594+ }
595+ PropertyChanges {
596+ target: closeAction
597+ enabled: false
598+ }
599+ PropertyChanges {
600+ target: addPrinterCol
601+ enabled: false
602+ }
603+ StateChangeScript {
604+ script: okTimer.start()
605+ }
606+ },
607+ State {
608+ name: "failure"
609+ PropertyChanges {
610+ target: errorMessageContainer
611+ visible: true
612+ }
613+ }
614+ ]
615+ header: PageHeader {
616+ title: "Add printer"
617+ flickable: addPrinterFlickable
618+ leadingActionBar.actions: [
619+ Action {
620+ id: closeAction
621+ iconName: "close"
622+ text: "Abort"
623+ onTriggered: pageStack.pop()
624+ }
625+ ]
626+ trailingActionBar {
627+ actions: [
628+ Action {
629+ id: okAction
630+ iconName: "ok"
631+ text: "Complete"
632+ onTriggered: {
633+ var ret;
634+ if (driverSelector.selectedIndex == 0) {
635+ ret = Printers.addPrinter(
636+ printerName.text,
637+ driversView.selectedDriver,
638+ printerUri.text,
639+ printerDescription.text,
640+ printerLocation.text
641+ );
642+ } else {
643+ ret = Printers.addPrinterWithPpdFile(
644+ printerName.text,
645+ printerPpd.text,
646+ printerUri.text,
647+ printerDescription.text,
648+ printerLocation.text
649+ );
650+ }
651+ if (ret) {
652+ addPrinterPage.state = "success"
653+ } else {
654+ errorMessage.text = Printers.lastMessage;
655+ addPrinterPage.state = "failure"
656+ }
657+ }
658+ }
659+ ]
660+ }
661+ }
662+
663+ Component.onCompleted: Printers.prepareToAddPrinter()
664+
665+ Timer {
666+ id: okTimer
667+ interval: 2000
668+ onTriggered: pageStack.pop();
669+ }
670+
671+ Flickable {
672+ id: addPrinterFlickable
673+ anchors.fill: parent
674+ contentHeight: contentItem.childrenRect.height
675+
676+ Column {
677+ id: addPrinterCol
678+ property bool enabled: true
679+ anchors { left: parent.left; right: parent.right }
680+
681+ Item {
682+ id: errorMessageContainer
683+ visible: false
684+ anchors {
685+ left: parent.left
686+ right: parent.right
687+ margins: units.gu(2)
688+ }
689+ height: units.gu(6)
690+ Label {
691+ id: errorMessage
692+ anchors {
693+ top: parent.top
694+ topMargin: units.gu(2)
695+ horizontalCenter: parent.horizontalCenter
696+ }
697+ }
698+
699+ }
700+
701+ ListItems.Standard {
702+ text: "Device URI"
703+ control: TextField {
704+ id: printerUri
705+ placeholderText: "ipp://server.local/my-queue"
706+ }
707+ enabled: parent.enabled
708+ }
709+
710+ ListItems.ValueSelector {
711+ id: driverSelector
712+ anchors { left: parent.left; right: parent.right }
713+ text: "Choose driver"
714+ values: [
715+ "Select printer from database",
716+ "Provide PPD file"
717+ ]
718+ enabled: parent.enabled
719+ }
720+
721+ ListItems.Standard {
722+ anchors { left: parent.left; right: parent.right }
723+ text: "Filter drivers"
724+ control: TextField {
725+ id: driverFilter
726+ onTextChanged: Printers.driverFilter = text
727+ }
728+ visible: driverSelector.selectedIndex == 0
729+ enabled: parent.enabled
730+ }
731+
732+ ListView {
733+ id: driversView
734+ property string selectedDriver
735+ property bool loading: true
736+ visible: driverSelector.selectedIndex == 0
737+ model: Printers.drivers
738+ anchors { left: parent.left; right: parent.right }
739+ height: units.gu(30)
740+ clip: true
741+ enabled: parent.enabled
742+ highlightFollowsCurrentItem: false
743+ highlight: Rectangle {
744+ z: 0
745+ y: driversView.currentItem.y
746+ width: driversView.currentItem.width
747+ height: driversView.currentItem.height
748+ color: theme.palette.selected.background
749+ }
750+ delegate: ListItem {
751+ height: driverLayout.height + (divider.visible ? divider.height : 0)
752+ ListItemLayout {
753+ id: driverLayout
754+ title.text: displayName
755+ subtitle.text: name
756+ summary.text: deviceId
757+ }
758+ onClicked: {
759+ driversView.selectedDriver = name
760+ driversView.currentIndex = index
761+ }
762+ }
763+
764+ ActivityIndicator {
765+ anchors.centerIn: parent
766+ running: parent.loading
767+ }
768+
769+ Connections {
770+ target: driversView
771+ onCountChanged: {
772+ target = null;
773+ driversView.loading = false;
774+ }
775+ }
776+ }
777+
778+ ListItems.Standard {
779+ text: "PPD File"
780+ visible: driverSelector.selectedIndex == 1
781+ control: TextField {
782+ id: printerPpd
783+ placeholderText: "/usr/share/cups/foo.ppd"
784+ }
785+ enabled: parent.enabled
786+ }
787+
788+ ListItems.Standard {
789+ anchors { left: parent.left; right: parent.right }
790+ text: "Printer name"
791+ control: TextField {
792+ id: printerName
793+ placeholderText: "laserjet"
794+ }
795+ enabled: parent.enabled
796+ }
797+
798+ ListItems.Standard {
799+ anchors { left: parent.left; right: parent.right }
800+ text: "Description (optional)"
801+ control: TextField {
802+ id: printerDescription
803+ placeholderText: "HP Laserjet with Duplexer"
804+ }
805+ enabled: parent.enabled
806+ }
807+
808+ ListItems.Standard {
809+ anchors { left: parent.left; right: parent.right }
810+ text: "Location (optional)"
811+ control: TextField {
812+ id: printerLocation
813+ placeholderText: "Lab 1"
814+ }
815+ enabled: parent.enabled
816+ }
817+ }
818+
819+ Label {
820+ id: remotePrintersLabel
821+ anchors {
822+ left: parent.left
823+ right: parent.right
824+ margins: units.gu(2)
825+ top: addPrinterCol.bottom
826+ }
827+ text: "Other printers"
828+
829+ ActivityIndicator {
830+ id: remotePrintersSearchIndicator
831+ anchors {
832+ right: parent.right
833+ verticalCenter: parent.verticalCenter
834+ }
835+ property var target
836+ Component.onCompleted: target = Printers.remotePrinters
837+ running: target.searching
838+ }
839+ }
840+
841+ ListView {
842+ id: remotePrintersList
843+ anchors {
844+ left: parent.left
845+ right: parent.right
846+ top: remotePrintersLabel.bottom
847+ topMargin: units.gu(2)
848+ }
849+ height: contentItem.childrenRect.height
850+ model: Printers.remotePrinters
851+ delegate: ListItem {
852+ height: modelLayout.height + (divider.visible ? divider.height : 0)
853+ ListItemLayout {
854+ id: modelLayout
855+ title.text: displayName
856+ subtitle.text: {
857+ if (type == PrinterEnum.LPDType) return "LPD";
858+ if (type == PrinterEnum.IppSType) return "IppS";
859+ if (type == PrinterEnum.Ipp14Type) return "Ipp14";
860+ if (type == PrinterEnum.HttpType) return "Http";
861+ if (type == PrinterEnum.BehType) return "Beh";
862+ if (type == PrinterEnum.SocketType) return "Socket";
863+ if (type == PrinterEnum.HttpsType) return "Https";
864+ if (type == PrinterEnum.IppType) return "Ipp";
865+ if (type == PrinterEnum.HPType) return "HP";
866+ if (type == PrinterEnum.USBType) return "USB";
867+ if (type == PrinterEnum.HPFaxType) return "HPFax";
868+ if (type == PrinterEnum.DNSSDType) return "DNSSD";
869+ else return "Unknown protocol";
870+ }
871+
872+ Icon {
873+ id: icon
874+ width: height
875+ height: units.gu(2.5)
876+ name: "network-printer-symbolic"
877+ SlotsLayout.position: SlotsLayout.First
878+ }
879+
880+ Button {
881+ text: "Select printer"
882+ onClicked: {
883+ var suggestedPrinterName = (" " + displayName).slice(1);
884+ suggestedPrinterName = suggestedPrinterName.replace(/\ /g, "\-");
885+ printerUri.text = uri;
886+ printerName.text = suggestedPrinterName;
887+ printerDescription.text = info;
888+ printerLocation.text = location;
889+ }
890+ }
891+ }
892+ }
893+ }
894+
895+ Label {
896+ anchors {
897+ left: parent.left
898+ right: parent.right
899+ top: remotePrintersLabel.bottom
900+ topMargin: units.gu(2)
901+ }
902+ text: "No other printers found"
903+ visible: !remotePrintersSearchIndicator.running && remotePrintersList.count == 0
904+ }
905+ }
906+ }
907+ }
908+}
909
910=== added directory 'modules/Ubuntu/Components/Extras/Printers'
911=== added file 'modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt'
912--- modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt 1970-01-01 00:00:00 +0000
913+++ modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt 2017-03-13 15:55:01 +0000
914@@ -0,0 +1,70 @@
915+project(UbuntuComponentsExtrasPrintersQml)
916+
917+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-permissive -pedantic -Wall -Wextra")
918+
919+add_definitions(-DUBUNTUCOMPONENTSEXTRASPRINTERS_LIBRARY)
920+
921+include(FindCups)
922+
923+include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CUPS_INCLUDE_DIR})
924+
925+find_package(Qt5Gui REQUIRED)
926+find_package(Qt5PrintSupport REQUIRED)
927+find_package(Qt5Qml REQUIRED)
928+find_package(Qt5DBus REQUIRED)
929+find_package(Qt5Concurrent REQUIRED)
930+
931+if(NOT CUPS_FOUND)
932+message(FATAL_ERROR "Could not find cups.")
933+endif()
934+
935+qt5_add_dbus_interface(
936+ GEN_SOURCES
937+ ${CMAKE_CURRENT_SOURCE_DIR}/org.cups.cupsd.Notifier.xml
938+ cupsdnotifier)
939+
940+add_library(UbuntuComponentsExtrasPrintersQml SHARED
941+ ${GEN_SOURCES}
942+ backend/backend.cpp
943+ backend/backend_cups.cpp
944+ backend/backend_pdf.cpp
945+
946+ cups/devicesearcher.cpp
947+ cups/ippclient.cpp
948+ cups/printerdriverloader.cpp
949+ cups/printerloader.cpp
950+
951+ models/devicemodel.cpp
952+ models/drivermodel.cpp
953+ models/jobmodel.cpp
954+ models/printermodel.cpp
955+
956+ printer/printer.cpp
957+ printer/printerjob.cpp
958+ printer/printersignalhandler.cpp
959+ printers/printers.cpp
960+
961+ enums.h
962+ i18n.cpp
963+ plugin.cpp
964+ structs.h
965+ utils.h
966+)
967+
968+target_link_libraries(UbuntuComponentsExtrasPrintersQml
969+ Qt5::DBus
970+ Qt5::Gui
971+ Qt5::PrintSupport
972+ Qt5::Qml
973+ Qt5::Concurrent
974+ ${CUPS_LIBRARIES}
975+)
976+
977+find_package(QmlPlugins)
978+
979+macro(add_plugin PLUGIN VERSION PATH)
980+ export_qmlfiles(${PLUGIN} ${PATH} DESTINATION ${QT_IMPORTS_DIR} ${ARGN})
981+ export_qmlplugin(${PLUGIN} ${VERSION} ${PATH} DESTINATION ${QT_IMPORTS_DIR} ${ARGN})
982+endmacro()
983+
984+add_plugin(Ubuntu.Components.Extras.Printers 0.1 Ubuntu/Components/Extras/Printers TARGETS UbuntuComponentsExtrasPrintersQml)
985
986=== added directory 'modules/Ubuntu/Components/Extras/Printers/backend'
987=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp'
988--- modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp 1970-01-01 00:00:00 +0000
989+++ modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp 2017-03-13 15:55:01 +0000
990@@ -0,0 +1,297 @@
991+/*
992+ * Copyright (C) 2017 Canonical, Ltd.
993+ *
994+ * This program is free software; you can redistribute it and/or modify
995+ * it under the terms of the GNU Lesser General Public License as published by
996+ * the Free Software Foundation; version 3.
997+ *
998+ * This program is distributed in the hope that it will be useful,
999+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1000+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1001+ * GNU Lesser General Public License for more details.
1002+ *
1003+ * You should have received a copy of the GNU Lesser General Public License
1004+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1005+ */
1006+
1007+#include "backend/backend.h"
1008+
1009+PrinterBackend::PrinterBackend(const QString &printerName, QObject *parent)
1010+ : QObject(parent)
1011+ , m_printerName(printerName)
1012+ , m_type(PrinterEnum::PrinterType::ProxyType)
1013+{
1014+}
1015+
1016+PrinterBackend::~PrinterBackend()
1017+{
1018+}
1019+
1020+bool PrinterBackend::holdsDefinition() const
1021+{
1022+ return false;
1023+}
1024+
1025+QString PrinterBackend::printerAdd(const QString &name,
1026+ const QString &uri,
1027+ const QString &ppdFile,
1028+ const QString &info,
1029+ const QString &location)
1030+{
1031+ Q_UNUSED(name);
1032+ Q_UNUSED(uri);
1033+ Q_UNUSED(ppdFile);
1034+ Q_UNUSED(info);
1035+ Q_UNUSED(location);
1036+ return QString();
1037+}
1038+
1039+QString PrinterBackend::printerAddWithPpd(const QString &name,
1040+ const QString &uri,
1041+ const QString &ppdFileName,
1042+ const QString &info,
1043+ const QString &location)
1044+{
1045+ Q_UNUSED(name);
1046+ Q_UNUSED(uri);
1047+ Q_UNUSED(ppdFileName);
1048+ Q_UNUSED(info);
1049+ Q_UNUSED(location);
1050+ return QString();
1051+}
1052+
1053+QString PrinterBackend::printerDelete(const QString &name)
1054+{
1055+ Q_UNUSED(name);
1056+ return QString();
1057+}
1058+
1059+QString PrinterBackend::printerSetDefault(const QString &name)
1060+{
1061+ Q_UNUSED(name);
1062+ return QString();
1063+}
1064+
1065+QString PrinterBackend::printerSetEnabled(const QString &name,
1066+ const bool enabled)
1067+{
1068+ Q_UNUSED(name);
1069+ Q_UNUSED(enabled);
1070+ return QString();
1071+}
1072+
1073+QString PrinterBackend::printerSetAcceptJobs(
1074+ const QString &name,
1075+ const bool enabled,
1076+ const QString &reason)
1077+{
1078+ Q_UNUSED(name);
1079+ Q_UNUSED(enabled);
1080+ Q_UNUSED(reason);
1081+ return QString();
1082+}
1083+
1084+QString PrinterBackend::printerSetShared(const QString &name,
1085+ const bool shared)
1086+{
1087+ Q_UNUSED(name);
1088+ Q_UNUSED(shared);
1089+ return QString();
1090+}
1091+
1092+QString PrinterBackend::printerSetInfo(const QString &name,
1093+ const QString &info)
1094+{
1095+ Q_UNUSED(name);
1096+ Q_UNUSED(info);
1097+ return QString();
1098+}
1099+
1100+QString PrinterBackend::printerAddOption(const QString &name,
1101+ const QString &option,
1102+ const QStringList &values)
1103+{
1104+ Q_UNUSED(name);
1105+ Q_UNUSED(option);
1106+ Q_UNUSED(values);
1107+ return QString();
1108+}
1109+
1110+QVariant PrinterBackend::printerGetOption(const QString &name,
1111+ const QString &option) const
1112+{
1113+ Q_UNUSED(name);
1114+ Q_UNUSED(option);
1115+ return QVariant();
1116+}
1117+
1118+QMap<QString, QVariant> PrinterBackend::printerGetOptions(
1119+ const QString &name, const QStringList &options) const
1120+{
1121+ Q_UNUSED(name);
1122+ Q_UNUSED(options);
1123+ return QMap<QString, QVariant>();
1124+}
1125+
1126+cups_dest_t* PrinterBackend::makeDest(const QString &name,
1127+ const PrinterJob *options)
1128+{
1129+ Q_UNUSED(name);
1130+ Q_UNUSED(options);
1131+ return Q_NULLPTR;
1132+}
1133+
1134+void PrinterBackend::cancelJob(const QString &name, const int jobId)
1135+{
1136+ Q_UNUSED(jobId);
1137+ Q_UNUSED(name);
1138+}
1139+
1140+void PrinterBackend::holdJob(const QString &name, const int jobId)
1141+{
1142+ Q_UNUSED(jobId);
1143+ Q_UNUSED(name);
1144+}
1145+
1146+void PrinterBackend::releaseJob(const QString &name, const int jobId)
1147+{
1148+ Q_UNUSED(jobId);
1149+ Q_UNUSED(name);
1150+}
1151+
1152+int PrinterBackend::printFileToDest(const QString &filepath,
1153+ const QString &title,
1154+ const cups_dest_t *dest)
1155+{
1156+ Q_UNUSED(filepath);
1157+ Q_UNUSED(title);
1158+ Q_UNUSED(dest);
1159+ return -1;
1160+}
1161+
1162+QList<QSharedPointer<PrinterJob>> PrinterBackend::printerGetJobs()
1163+{
1164+ return QList<QSharedPointer<PrinterJob>>{};
1165+}
1166+
1167+QMap<QString, QVariant> PrinterBackend::printerGetJobAttributes(
1168+ const QString &name, const int jobId)
1169+{
1170+ Q_UNUSED(name);
1171+ Q_UNUSED(jobId);
1172+ return QMap<QString, QVariant>();
1173+}
1174+
1175+QString PrinterBackend::printerName() const
1176+{
1177+ return m_printerName;
1178+}
1179+
1180+QString PrinterBackend::description() const
1181+{
1182+ return QString();
1183+}
1184+
1185+QString PrinterBackend::location() const
1186+{
1187+ return QString();
1188+}
1189+
1190+QString PrinterBackend::makeAndModel() const
1191+{
1192+ return QString();
1193+}
1194+
1195+bool PrinterBackend::isRemote() const
1196+{
1197+ return false;
1198+}
1199+
1200+PrinterEnum::State PrinterBackend::state() const
1201+{
1202+ return PrinterEnum::State::IdleState;
1203+}
1204+
1205+QList<QPageSize> PrinterBackend::supportedPageSizes() const
1206+{
1207+ return QList<QPageSize>();
1208+}
1209+
1210+QPageSize PrinterBackend::defaultPageSize() const
1211+{
1212+ return QPageSize();
1213+}
1214+
1215+bool PrinterBackend::supportsCustomPageSizes() const
1216+{
1217+ return false;
1218+}
1219+
1220+QPageSize PrinterBackend::minimumPhysicalPageSize() const
1221+{
1222+ return QPageSize();
1223+}
1224+
1225+QPageSize PrinterBackend::maximumPhysicalPageSize() const
1226+{
1227+ return QPageSize();
1228+}
1229+
1230+QList<int> PrinterBackend::supportedResolutions() const
1231+{
1232+ return QList<int>();
1233+}
1234+
1235+PrinterEnum::DuplexMode PrinterBackend::defaultDuplexMode() const
1236+{
1237+ return PrinterEnum::DuplexMode::DuplexNone;
1238+}
1239+
1240+QList<PrinterEnum::DuplexMode> PrinterBackend::supportedDuplexModes() const
1241+{
1242+ return QList<PrinterEnum::DuplexMode>();
1243+}
1244+
1245+QList<QSharedPointer<Printer>> PrinterBackend::availablePrinters()
1246+{
1247+ return QList<QSharedPointer<Printer>>();
1248+}
1249+
1250+QStringList PrinterBackend::availablePrinterNames()
1251+{
1252+ return QStringList();
1253+}
1254+
1255+QSharedPointer<Printer> PrinterBackend::getPrinter(const QString &printerName)
1256+{
1257+ Q_UNUSED(printerName);
1258+ return QSharedPointer<Printer>(Q_NULLPTR);
1259+}
1260+
1261+QString PrinterBackend::defaultPrinterName()
1262+{
1263+ return QString();
1264+}
1265+
1266+void PrinterBackend::requestPrinterDrivers()
1267+{
1268+}
1269+
1270+void PrinterBackend::requestPrinter(const QString &printerName)
1271+{
1272+ Q_UNUSED(printerName);
1273+}
1274+
1275+PrinterEnum::PrinterType PrinterBackend::type() const
1276+{
1277+ return m_type;
1278+}
1279+
1280+void PrinterBackend::setPrinterNameInternal(const QString &printerName)
1281+{
1282+ m_printerName = printerName;
1283+}
1284+
1285+void PrinterBackend::refresh()
1286+{
1287+}
1288
1289=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend.h'
1290--- modules/Ubuntu/Components/Extras/Printers/backend/backend.h 1970-01-01 00:00:00 +0000
1291+++ modules/Ubuntu/Components/Extras/Printers/backend/backend.h 2017-03-13 15:55:01 +0000
1292@@ -0,0 +1,208 @@
1293+/*
1294+ * Copyright (C) 2017 Canonical, Ltd.
1295+ *
1296+ * This program is free software; you can redistribute it and/or modify
1297+ * it under the terms of the GNU Lesser General Public License as published by
1298+ * the Free Software Foundation; version 3.
1299+ *
1300+ * This program is distributed in the hope that it will be useful,
1301+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1302+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1303+ * GNU Lesser General Public License for more details.
1304+ *
1305+ * You should have received a copy of the GNU Lesser General Public License
1306+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1307+ */
1308+
1309+#ifndef USC_PRINTERS_BACKEND_H
1310+#define USC_PRINTERS_BACKEND_H
1311+
1312+#include "printer/printer.h"
1313+#include "printer/printerjob.h"
1314+
1315+// TODO: remove cups specific things from this API
1316+#include <cups/cups.h>
1317+
1318+#include <QObject>
1319+#include <QPageSize>
1320+#include <QList>
1321+#include <QString>
1322+#include <QStringList>
1323+
1324+class Printer;
1325+class PrinterJob;
1326+class PRINTERS_DECL_EXPORT PrinterBackend : public QObject
1327+{
1328+ Q_OBJECT
1329+public:
1330+ explicit PrinterBackend(QObject *parent = Q_NULLPTR);
1331+ explicit PrinterBackend(const QString &printerName,
1332+ QObject *parent = Q_NULLPTR);
1333+ virtual ~PrinterBackend();
1334+
1335+ virtual bool holdsDefinition() const;
1336+
1337+ // Add a printer using an already existing ppd.
1338+ virtual QString printerAdd(const QString &name,
1339+ const QString &uri,
1340+ const QString &ppdFile,
1341+ const QString &info,
1342+ const QString &location);
1343+
1344+ // Add a printer and provide a ppd file.
1345+ virtual QString printerAddWithPpd(const QString &name,
1346+ const QString &uri,
1347+ const QString &ppdFileName,
1348+ const QString &info,
1349+ const QString &location);
1350+ virtual QString printerDelete(const QString &name);
1351+ virtual QString printerSetDefault(const QString &name);
1352+ virtual QString printerSetEnabled(const QString &name,
1353+ const bool enabled);
1354+ virtual QString printerSetAcceptJobs(
1355+ const QString &name,
1356+ const bool accept,
1357+ const QString &reason = QString::null);
1358+ virtual QString printerSetShared(const QString &name,
1359+ const bool shared);
1360+ virtual QString printerSetInfo(const QString &name,
1361+ const QString &info);
1362+ virtual QString printerAddOption(const QString &name,
1363+ const QString &option,
1364+ const QStringList &values);
1365+
1366+ virtual QVariant printerGetOption(const QString &name,
1367+ const QString &option) const;
1368+ virtual QMap<QString, QVariant> printerGetOptions(
1369+ const QString &name, const QStringList &options) const;
1370+ virtual cups_dest_t* makeDest(const QString &name,
1371+ const PrinterJob *options);
1372+
1373+ virtual void cancelJob(const QString &name, const int jobId);
1374+ virtual void holdJob(const QString &name, const int jobId);
1375+ virtual void releaseJob(const QString &name, const int jobId);
1376+ virtual int printFileToDest(const QString &filepath,
1377+ const QString &title,
1378+ const cups_dest_t *dest);
1379+ virtual QList<QSharedPointer<PrinterJob>> printerGetJobs();
1380+ virtual QMap<QString, QVariant> printerGetJobAttributes(
1381+ const QString &name, const int jobId);
1382+
1383+ virtual QString printerName() const;
1384+ virtual QString description() const;
1385+ virtual QString location() const;
1386+ virtual QString makeAndModel() const;
1387+ virtual bool isRemote() const;
1388+
1389+ virtual PrinterEnum::State state() const;
1390+ virtual QList<QPageSize> supportedPageSizes() const;
1391+ virtual QPageSize defaultPageSize() const;
1392+ virtual bool supportsCustomPageSizes() const;
1393+
1394+ virtual QPageSize minimumPhysicalPageSize() const;
1395+ virtual QPageSize maximumPhysicalPageSize() const;
1396+ virtual QList<int> supportedResolutions() const;
1397+ virtual PrinterEnum::DuplexMode defaultDuplexMode() const;
1398+ virtual QList<PrinterEnum::DuplexMode> supportedDuplexModes() const;
1399+
1400+ virtual QList<QSharedPointer<Printer>> availablePrinters();
1401+ virtual QStringList availablePrinterNames();
1402+ virtual QSharedPointer<Printer> getPrinter(const QString &printerName);
1403+ virtual QString defaultPrinterName();
1404+
1405+ virtual void requestPrinterDrivers();
1406+ virtual void requestPrinter(const QString &printerName);
1407+
1408+ virtual PrinterEnum::PrinterType type() const;
1409+
1410+ virtual void setPrinterNameInternal(const QString &printerName);
1411+
1412+public Q_SLOTS:
1413+ virtual void refresh();
1414+
1415+Q_SIGNALS:
1416+ void printerDriversLoaded(const QList<PrinterDriver> &drivers);
1417+ void printerDriversFailedToLoad(const QString &errorMessage);
1418+
1419+ void printerLoaded(QSharedPointer<Printer> printers);
1420+ void deviceFound(const Device &device);
1421+ void deviceSearchFinished();
1422+
1423+ void jobCompleted(
1424+ const QString &text,
1425+ const QString &printerUri,
1426+ const QString &printerName,
1427+ uint printerState,
1428+ const QString &printerStateReason,
1429+ bool acceptingJobs,
1430+ uint jobId,
1431+ uint jobState,
1432+ const QString &jobStateReason,
1433+ const QString &job_name,
1434+ uint jobImpressionsCompleted
1435+ );
1436+ void jobCreated(
1437+ const QString &text,
1438+ const QString &printerUri,
1439+ const QString &printerName,
1440+ uint printerState,
1441+ const QString &printerStateReason,
1442+ bool acceptingJobs,
1443+ uint jobId,
1444+ uint jobState,
1445+ const QString &jobStateReason,
1446+ const QString &job_name,
1447+ uint jobImpressionsCompleted
1448+ );
1449+ void jobState(
1450+ const QString &text,
1451+ const QString &printerUri,
1452+ const QString &printerName,
1453+ uint printerState,
1454+ const QString &printerStateReason,
1455+ bool acceptingJobs,
1456+ uint jobId,
1457+ uint jobState,
1458+ const QString &jobStateReason,
1459+ const QString &job_name,
1460+ uint jobImpressionsCompleted
1461+ );
1462+ void printerAdded(
1463+ const QString &text,
1464+ const QString &printerUri,
1465+ const QString &printerName,
1466+ uint printerState,
1467+ const QString &printerStateReason,
1468+ bool acceptingJobs
1469+ );
1470+ void printerDeleted(
1471+ const QString &text,
1472+ const QString &printerUri,
1473+ const QString &printerName,
1474+ uint printerState,
1475+ const QString &printerStateReason,
1476+ bool acceptingJobs
1477+ );
1478+ void printerModified(
1479+ const QString &text,
1480+ const QString &printerUri,
1481+ const QString &printerName,
1482+ uint printerState,
1483+ const QString &printerStateReason,
1484+ bool acceptingJobs
1485+ );
1486+ void printerStateChanged(
1487+ const QString &text,
1488+ const QString &printerUri,
1489+ const QString &printerName,
1490+ uint printerState,
1491+ const QString &printerStateReason,
1492+ bool acceptingJobs
1493+ );
1494+
1495+protected:
1496+ QString m_printerName;
1497+ PrinterEnum::PrinterType m_type;
1498+};
1499+
1500+#endif // USC_PRINTERS_BACKEND_H
1501
1502=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp'
1503--- modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp 1970-01-01 00:00:00 +0000
1504+++ modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp 2017-03-13 15:55:01 +0000
1505@@ -0,0 +1,795 @@
1506+/*
1507+ * Copyright (C) 2017 Canonical, Ltd.
1508+ *
1509+ * This program is free software; you can redistribute it and/or modify
1510+ * it under the terms of the GNU Lesser General Public License as published by
1511+ * the Free Software Foundation; version 3.
1512+ *
1513+ * This program is distributed in the hope that it will be useful,
1514+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1515+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1516+ * GNU Lesser General Public License for more details.
1517+ *
1518+ * You should have received a copy of the GNU Lesser General Public License
1519+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1520+ */
1521+
1522+#include "backend/backend_cups.h"
1523+#include "cups/devicesearcher.h"
1524+#include "cups/printerdriverloader.h"
1525+#include "cups/printerloader.h"
1526+#include "utils.h"
1527+
1528+#include <cups/http.h>
1529+#include <cups/ipp.h>
1530+#include <cups/ppd.h>
1531+
1532+#include <QLocale>
1533+#include <QThread>
1534+#include <QTimeZone>
1535+
1536+#define __CUPS_ADD_OPTION(dest, name, value) dest->num_options = \
1537+ cupsAddOption(name, value, dest->num_options, &dest->options);
1538+
1539+#define __CUPS_ATTR_EXISTS(map, attr, type) map.contains(attr) \
1540+ && map.value(attr).canConvert<type>()
1541+
1542+PrinterCupsBackend::PrinterCupsBackend(IppClient *client, QPrinterInfo info,
1543+ OrgCupsCupsdNotifierInterface *notifier,
1544+ QObject *parent)
1545+ : PrinterBackend(info.printerName(), parent)
1546+ , m_knownQualityOptions({
1547+ "Quality", "PrintQuality", "HPPrintQuality", "StpQuality",
1548+ "OutputMode",})
1549+ , m_extendedAttributeNames({"StateMessage", "DeviceUri", "IsShared"})
1550+ , m_client(client)
1551+ , m_info(info)
1552+ , m_notifier(notifier)
1553+ , m_cupsSubscriptionId(-1)
1554+{
1555+ m_type = PrinterEnum::PrinterType::CupsType;
1556+ connect(m_notifier, SIGNAL(JobCompleted(const QString&, const QString&,
1557+ const QString&, uint,
1558+ const QString&, bool, uint, uint,
1559+ const QString&, const QString&, uint)),
1560+ this, SIGNAL(jobCompleted(const QString&, const QString&,
1561+ const QString&, uint, const QString&,
1562+ bool, uint, uint, const QString&,
1563+ const QString&, uint)));
1564+ connect(m_notifier, SIGNAL(JobCreated(const QString&, const QString&,
1565+ const QString&, uint, const QString&,
1566+ bool, uint, uint, const QString&,
1567+ const QString&, uint)),
1568+ this, SIGNAL(jobCreated(const QString&, const QString&,
1569+ const QString&, uint, const QString&, bool,
1570+ uint, uint, const QString&, const QString&,
1571+ uint)));
1572+ connect(m_notifier, SIGNAL(JobState(const QString&, const QString&,
1573+ const QString&, uint, const QString&,
1574+ bool, uint, uint, const QString&,
1575+ const QString&, uint)),
1576+ this, SIGNAL(jobState(const QString&, const QString&,
1577+ const QString&, uint, const QString&, bool,
1578+ uint, uint, const QString&, const QString&,
1579+ uint)));
1580+ connect(m_notifier, SIGNAL(PrinterAdded(const QString&, const QString&,
1581+ const QString&, uint,
1582+ const QString&, bool)),
1583+ this, SIGNAL(printerAdded(const QString&, const QString&,
1584+ const QString&, uint,
1585+ const QString&, bool)));
1586+ connect(m_notifier, SIGNAL(PrinterDeleted(const QString&, const QString&,
1587+ const QString&, uint,
1588+ const QString&, bool)),
1589+ this, SIGNAL(printerDeleted(const QString&, const QString&,
1590+ const QString&, uint,
1591+ const QString&, bool)));
1592+ connect(m_notifier, SIGNAL(PrinterModified(const QString&, const QString&,
1593+ const QString&, uint,
1594+ const QString&, bool)),
1595+ this, SIGNAL(printerModified(const QString&, const QString&,
1596+ const QString&, uint,
1597+ const QString&, bool)));
1598+ connect(m_notifier, SIGNAL(PrinterStateChanged(const QString&,
1599+ const QString&,
1600+ const QString&, uint,
1601+ const QString&, bool)),
1602+ this, SIGNAL(printerStateChanged(const QString&, const QString&,
1603+ const QString&, uint,
1604+ const QString&, bool)));
1605+
1606+}
1607+
1608+PrinterCupsBackend::~PrinterCupsBackend()
1609+{
1610+ Q_FOREACH(auto dest, m_dests) {
1611+ if (dest)
1612+ cupsFreeDests(1, dest);
1613+ }
1614+ Q_FOREACH(auto ppd, m_ppds) {
1615+ if (ppd)
1616+ ppdClose(ppd);
1617+ }
1618+
1619+ cancelSubscription();
1620+ Q_EMIT cancelWorkers();
1621+}
1622+
1623+QString PrinterCupsBackend::printerAdd(const QString &name,
1624+ const QString &uri,
1625+ const QString &ppdFile,
1626+ const QString &info,
1627+ const QString &location)
1628+{
1629+ if (!m_client->printerAdd(name, uri, ppdFile, info, location)) {
1630+ return m_client->getLastError();
1631+ }
1632+ return QString();
1633+}
1634+
1635+QString PrinterCupsBackend::printerAddWithPpd(const QString &name,
1636+ const QString &uri,
1637+ const QString &ppdFileName,
1638+ const QString &info,
1639+ const QString &location)
1640+{
1641+ if (!m_client->printerAddWithPpdFile(name, uri, ppdFileName, info, location)) {
1642+ return m_client->getLastError();
1643+ }
1644+ return QString();
1645+}
1646+
1647+bool PrinterCupsBackend::holdsDefinition() const
1648+{
1649+ return !m_info.isNull();
1650+}
1651+
1652+QString PrinterCupsBackend::printerDelete(const QString &name)
1653+{
1654+ if (!m_client->printerDelete(name)) {
1655+ return m_client->getLastError();
1656+ }
1657+ return QString();
1658+}
1659+
1660+QString PrinterCupsBackend::printerSetDefault(const QString &name)
1661+{
1662+ if (!m_client->printerSetDefault(name)) {
1663+ return m_client->getLastError();
1664+ }
1665+ return QString();
1666+}
1667+
1668+QString PrinterCupsBackend::printerSetEnabled(const QString &name,
1669+ const bool enabled)
1670+{
1671+ if (!m_client->printerSetEnabled(name, enabled)) {
1672+ return m_client->getLastError();
1673+ }
1674+ return QString();
1675+}
1676+
1677+QString PrinterCupsBackend::printerSetAcceptJobs(
1678+ const QString &name,
1679+ const bool accept,
1680+ const QString &reason)
1681+{
1682+ if (!m_client->printerSetAcceptJobs(name, accept, reason)) {
1683+ return m_client->getLastError();
1684+ }
1685+ return QString();
1686+}
1687+
1688+QString PrinterCupsBackend::printerSetShared(const QString &name,
1689+ const bool shared)
1690+{
1691+ if (!m_client->printerSetShared(name, shared)) {
1692+ return m_client->getLastError();
1693+ }
1694+ return QString();
1695+}
1696+
1697+
1698+QString PrinterCupsBackend::printerSetInfo(const QString &name,
1699+ const QString &info)
1700+{
1701+ if (!m_client->printerClassSetInfo(name, info)) {
1702+ return m_client->getLastError();
1703+ }
1704+ return QString();
1705+}
1706+
1707+QString PrinterCupsBackend::printerAddOption(const QString &name,
1708+ const QString &option,
1709+ const QStringList &values)
1710+{
1711+ if (!m_client->printerClassSetOption(name, option, values)) {
1712+ return m_client->getLastError();
1713+ }
1714+
1715+ return QString();
1716+}
1717+
1718+QVariant PrinterCupsBackend::printerGetOption(const QString &name,
1719+ const QString &option) const
1720+{
1721+ auto res = printerGetOptions(name, QStringList({option}));
1722+ return res[option];
1723+}
1724+
1725+QMap<QString, QVariant> PrinterCupsBackend::printerGetOptions(
1726+ const QString &name, const QStringList &options) const
1727+{
1728+ QMap<QString, QVariant> ret;
1729+
1730+ cups_dest_t *dest = getDest(name);
1731+ ppd_file_t* ppd = getPpd(name);
1732+
1733+ // Used to store extended attributes, which we should request maximum once.
1734+ QMap<QString, QVariant> extendedAttributesResults;
1735+
1736+ /* Goes through known extended attributes. If one is being asked for,
1737+ ask for all of them right away. */
1738+ Q_FOREACH(const QString &extendedOption, m_extendedAttributeNames) {
1739+ if (options.contains(extendedOption)) {
1740+ extendedAttributesResults = m_client->printerGetAttributes(
1741+ name, QStringList({
1742+ "device-uri", "printer-uri-supported",
1743+ "printer-state-message"})
1744+ );
1745+ break;
1746+ }
1747+ }
1748+
1749+ Q_FOREACH(const QString &option, options) {
1750+ if (option == QStringLiteral("DefaultColorModel") && ppd) {
1751+ ColorModel model;
1752+ ppd_option_t *ppdColorModel = ppdFindOption(ppd, "ColorModel");
1753+ if (ppdColorModel) {
1754+ ppd_choice_t* def = ppdFindChoice(ppdColorModel,
1755+ ppdColorModel->defchoice);
1756+ if (def) {
1757+ model = Utils::parsePpdColorModel(def->choice,
1758+ def->text,
1759+ "ColorModel");
1760+ }
1761+ }
1762+ ret[option] = QVariant::fromValue(model);
1763+ } else if (option == QStringLiteral("DefaultPrintQuality") && ppd) {
1764+ PrintQuality quality;
1765+ Q_FOREACH(const QString opt, m_knownQualityOptions) {
1766+ ppd_option_t *ppdQuality = ppdFindOption(ppd, opt.toUtf8());
1767+ if (ppdQuality) {
1768+ ppd_choice_t* def = ppdFindChoice(ppdQuality,
1769+ ppdQuality->defchoice);
1770+ if (def) {
1771+ quality = Utils::parsePpdPrintQuality(def->choice,
1772+ def->text, opt);
1773+ }
1774+ }
1775+ }
1776+ ret[option] = QVariant::fromValue(quality);
1777+ } else if (option == QStringLiteral("SupportedPrintQualities") && ppd) {
1778+ QList<PrintQuality> qualities;
1779+ Q_FOREACH(const QString &opt, m_knownQualityOptions) {
1780+ ppd_option_t *qualityOpt = ppdFindOption(ppd, opt.toUtf8());
1781+ if (qualityOpt) {
1782+ for (int i = 0; i < qualityOpt->num_choices; ++i) {
1783+ qualities.append(
1784+ Utils::parsePpdPrintQuality(
1785+ qualityOpt->choices[i].choice,
1786+ qualityOpt->choices[i].text,
1787+ opt
1788+ )
1789+ );
1790+ }
1791+ }
1792+ }
1793+ ret[option] = QVariant::fromValue(qualities);
1794+ } else if (option == QStringLiteral("SupportedColorModels") && ppd) {
1795+ QList<ColorModel> models;
1796+ ppd_option_t *colorModels = ppdFindOption(ppd, "ColorModel");
1797+ if (colorModels) {
1798+ for (int i = 0; i < colorModels->num_choices; ++i) {
1799+ models.append(
1800+ Utils::parsePpdColorModel(
1801+ colorModels->choices[i].choice,
1802+ colorModels->choices[i].text,
1803+ QStringLiteral("ColorModel")
1804+ )
1805+ );
1806+ }
1807+ }
1808+ ret[option] = QVariant::fromValue(models);
1809+ } else if (option == QStringLiteral("AcceptJobs") && dest) {
1810+ // "true" if the destination is accepting new jobs, "false" if not.
1811+ QString res = cupsGetOption("printer-is-accepting-jobs",
1812+ dest->num_options, dest->options);
1813+ ret[option] = res.contains("true");
1814+ } else if (option == QStringLiteral("StateReasons") && dest) {
1815+ ret[option] = cupsGetOption("printer-state-reasons",
1816+ dest->num_options, dest->options);
1817+ } else if (option == QStringLiteral("StateMessage")) {
1818+ ret[option] = extendedAttributesResults["printer-state-message"];
1819+ } else if (option == QStringLiteral("DeviceUri")) {
1820+ auto res = extendedAttributesResults;
1821+ if (!res["printer-uri-supported"].toString().isEmpty()) {
1822+ ret[option] = res["printer-uri-supported"];
1823+ }
1824+ if (!res["device-uri"].toString().isEmpty()) {
1825+ ret[option] = res["device-uri"];
1826+ }
1827+ } else if (option == QStringLiteral("Shared") && dest) {
1828+ ret[option] = cupsGetOption("printer-is-shared",
1829+ dest->num_options, dest->options);
1830+ }
1831+ }
1832+ return ret;
1833+}
1834+
1835+cups_dest_t* PrinterCupsBackend::makeDest(const QString &name,
1836+ const PrinterJob *options)
1837+{
1838+ cups_dest_t *dest = getDest(name);
1839+
1840+ if (options->collate()) {
1841+ __CUPS_ADD_OPTION(dest, "Collate", "True");
1842+ } else {
1843+ __CUPS_ADD_OPTION(dest, "Collate", "False");
1844+ }
1845+
1846+ if (options->copies() > 1) {
1847+ __CUPS_ADD_OPTION(dest, "copies", QString::number(options->copies()).toLocal8Bit());
1848+ }
1849+
1850+ __CUPS_ADD_OPTION(dest, "ColorModel", options->getColorModel().name.toLocal8Bit());
1851+ __CUPS_ADD_OPTION(dest, "Duplex", Utils::duplexModeToPpdChoice(options->getDuplexMode()).toLocal8Bit());
1852+
1853+ if (options->landscape()) {
1854+ __CUPS_ADD_OPTION(dest, "landscape", "");
1855+ }
1856+
1857+ if (options->printRangeMode() == PrinterEnum::PrintRange::PageRange
1858+ && !options->printRange().isEmpty()) {
1859+ __CUPS_ADD_OPTION(dest, "page-ranges", options->printRange().toLocal8Bit());
1860+ }
1861+
1862+ PrintQuality quality = options->getPrintQuality();
1863+ __CUPS_ADD_OPTION(dest, quality.originalOption.toLocal8Bit(),
1864+ quality.name.toLocal8Bit());
1865+
1866+ if (options->reverse()) {
1867+ __CUPS_ADD_OPTION(dest, "OutputOrder", "Reverse");
1868+ } else {
1869+ __CUPS_ADD_OPTION(dest, "OutputOrder", "Normal");
1870+ }
1871+
1872+ // Always scale to fit the page for now
1873+ __CUPS_ADD_OPTION(dest, "fit-to-page", "True");
1874+
1875+ return dest;
1876+}
1877+
1878+void PrinterCupsBackend::cancelJob(const QString &name, const int jobId)
1879+{
1880+ int ret = cupsCancelJob(name.toLocal8Bit(), jobId);
1881+
1882+ if (!ret) {
1883+ qWarning() << "Failed to cancel job:" << jobId << "for" << name;
1884+ }
1885+}
1886+
1887+void PrinterCupsBackend::holdJob(const QString &name, const int jobId)
1888+{
1889+ if (!m_client->printerHoldJob(name, jobId)) {
1890+ qWarning() << "Failed to hold job:" << jobId << "for" << name;
1891+ }
1892+}
1893+
1894+void PrinterCupsBackend::releaseJob(const QString &name, const int jobId)
1895+{
1896+ if (!m_client->printerReleaseJob(name, jobId)) {
1897+ qWarning() << "Failed to release job:" << jobId << "for" << name;
1898+ }
1899+}
1900+
1901+int PrinterCupsBackend::printFileToDest(const QString &filepath,
1902+ const QString &title,
1903+ const cups_dest_t *dest)
1904+{
1905+ qDebug() << "Printing:" << filepath << title << dest->name << dest->num_options;
1906+ return cupsPrintFile(dest->name,
1907+ filepath.toLocal8Bit(),
1908+ title.toLocal8Bit(),
1909+ dest->num_options,
1910+ dest->options);
1911+}
1912+
1913+
1914+QList<cups_job_t *> PrinterCupsBackend::getCupsJobs(const QString &name)
1915+{
1916+ QList<cups_job_t *> list;
1917+ cups_job_t *jobs;
1918+
1919+ // Get a list of the jobs that are 'mine' and only active ones
1920+ // https://www.cups.org/doc/api-cups.html#cupsGetJobs
1921+ int count;
1922+ if (name.isEmpty()) {
1923+ count = cupsGetJobs(&jobs, NULL, 1, CUPS_WHICHJOBS_ACTIVE);
1924+ } else {
1925+ count = cupsGetJobs(&jobs, name.toLocal8Bit(), 1, CUPS_WHICHJOBS_ACTIVE);
1926+ }
1927+
1928+ for (int i=0; i < count; i++) {
1929+ list.append(&jobs[i]);
1930+ }
1931+
1932+ return list;
1933+}
1934+
1935+QMap<QString, QVariant> PrinterCupsBackend::printerGetJobAttributes(
1936+ const QString &name, const int jobId)
1937+{
1938+ Q_UNUSED(name);
1939+ QMap<QString, QVariant> rawMap = m_client->printerGetJobAttributes(name, jobId);
1940+ QMap<QString, QVariant> map;
1941+
1942+ // Filter attributes to know values
1943+ // Do this here so we can use things such as m_knownQualityOptions
1944+
1945+ if (__CUPS_ATTR_EXISTS(rawMap, "Collate", bool)) {
1946+ map.insert("Collate", rawMap.value("Collate"));
1947+ } else {
1948+ map.insert("Collate", QVariant(true));
1949+ }
1950+
1951+ if (__CUPS_ATTR_EXISTS(rawMap, "copies", int)) {
1952+ map.insert("copies", rawMap.value("copies"));
1953+ } else {
1954+ map.insert("copies", QVariant(1));
1955+ }
1956+
1957+ if (__CUPS_ATTR_EXISTS(rawMap, "ColorModel", QString)) {
1958+ map.insert("ColorModel", rawMap.value("ColorModel"));
1959+ } else {
1960+ map.insert("ColorModel", QVariant(""));
1961+ }
1962+
1963+ if (__CUPS_ATTR_EXISTS(rawMap, "Duplex", QString)) {
1964+ map.insert("Duplex", rawMap.value("Duplex"));
1965+ } else {
1966+ map.insert("Duplex", QVariant(""));
1967+ }
1968+
1969+ if (__CUPS_ATTR_EXISTS(rawMap, "landscape", bool)) {
1970+ map.insert("landscape", rawMap.value("landscape"));
1971+ } else {
1972+ map.insert("landscape", QVariant(false));
1973+ }
1974+
1975+ if (__CUPS_ATTR_EXISTS(rawMap, "page-ranges", QList<QVariant>)) {
1976+ QList<QVariant> range = rawMap.value("page-ranges").toList();
1977+ QStringList rangeStrings;
1978+
1979+ Q_FOREACH(QVariant var, range) {
1980+ rangeStrings << var.toString();
1981+ }
1982+
1983+ map.insert("page-ranges", QVariant(rangeStrings));
1984+ } else {
1985+ map.insert("page-ranges", QVariant(QStringList()));
1986+ }
1987+
1988+ Q_FOREACH(QString qualityOption, m_knownQualityOptions) {
1989+ if (rawMap.contains(qualityOption)
1990+ && rawMap.value(qualityOption).canConvert<QString>()) {
1991+ map.insert("quality", rawMap.value(qualityOption).toString());
1992+ }
1993+ }
1994+
1995+ if (!map.contains("quality")) {
1996+ map.insert("quality", QVariant(""));
1997+ }
1998+
1999+ if (__CUPS_ATTR_EXISTS(rawMap, "OutputOrder", QString)) {
2000+ map.insert("OutputOrder", rawMap.value("OutputOrder"));
2001+ } else {
2002+ map.insert("OutputOrder", "Normal");
2003+ }
2004+
2005+ // Generate a list of messages
2006+ // TODO: for now just using job-printer-state-message, are there others?
2007+ QStringList messages;
2008+
2009+ if (__CUPS_ATTR_EXISTS(rawMap, "job-printer-state-message", QString)) {
2010+ messages << rawMap.value("job-printer-state-message").toString();
2011+ }
2012+
2013+ map.insert("messages", QVariant(messages));
2014+
2015+ return map;
2016+}
2017+
2018+
2019+QList<QSharedPointer<PrinterJob>> PrinterCupsBackend::printerGetJobs()
2020+{
2021+ auto jobs = getCupsJobs();
2022+ QList<QSharedPointer<PrinterJob>> list;
2023+
2024+ Q_FOREACH(auto job, jobs) {
2025+ auto newJob = QSharedPointer<PrinterJob>(
2026+ new PrinterJob(QString::fromUtf8(job->dest), this, job->id)
2027+ );
2028+
2029+ // Extract the times
2030+ QDateTime completedTime;
2031+ completedTime.setTimeZone(QTimeZone::systemTimeZone());
2032+ completedTime.setTime_t(job->completed_time);
2033+
2034+ QDateTime creationTime;
2035+ creationTime.setTimeZone(QTimeZone::systemTimeZone());
2036+ creationTime.setTime_t(job->creation_time);
2037+
2038+ QDateTime processingTime;
2039+ processingTime.setTimeZone(QTimeZone::systemTimeZone());
2040+ processingTime.setTime_t(job->processing_time);
2041+
2042+ // Load the information from the cups struct
2043+ newJob->setCompletedTime(completedTime);
2044+ newJob->setCreationTime(creationTime);
2045+ newJob->setProcessingTime(processingTime);
2046+ newJob->setSize(job->size);
2047+ newJob->setState(static_cast<PrinterEnum::JobState>(job->state));
2048+ newJob->setTitle(QString::fromLocal8Bit(job->title));
2049+ newJob->setUser(QString::fromLocal8Bit(job->user));
2050+
2051+ list.append(newJob);
2052+ }
2053+ if (!list.isEmpty())
2054+ cupsFreeJobs(list.size(), jobs.first());
2055+
2056+ return list;
2057+}
2058+
2059+QString PrinterCupsBackend::printerName() const
2060+{
2061+ return m_printerName;
2062+}
2063+
2064+QString PrinterCupsBackend::description() const
2065+{
2066+ return m_info.description();
2067+}
2068+
2069+QString PrinterCupsBackend::location() const
2070+{
2071+ return m_info.location();
2072+}
2073+
2074+QString PrinterCupsBackend::makeAndModel() const
2075+{
2076+ return m_info.makeAndModel();
2077+}
2078+
2079+bool PrinterCupsBackend::isRemote() const
2080+{
2081+ return m_info.isRemote();
2082+}
2083+
2084+PrinterEnum::State PrinterCupsBackend::state() const
2085+{
2086+ switch (m_info.state()) {
2087+ case QPrinter::Active:
2088+ return PrinterEnum::State::ActiveState;
2089+ case QPrinter::Aborted:
2090+ return PrinterEnum::State::AbortedState;
2091+ case QPrinter::Error:
2092+ return PrinterEnum::State::ErrorState;
2093+ case QPrinter::Idle:
2094+ default:
2095+ return PrinterEnum::State::IdleState;
2096+ }
2097+}
2098+
2099+QList<QPageSize> PrinterCupsBackend::supportedPageSizes() const
2100+{
2101+ return m_info.supportedPageSizes();
2102+}
2103+
2104+QPageSize PrinterCupsBackend::defaultPageSize() const
2105+{
2106+ return m_info.defaultPageSize();
2107+}
2108+
2109+bool PrinterCupsBackend::supportsCustomPageSizes() const
2110+{
2111+ return m_info.supportsCustomPageSizes();
2112+}
2113+
2114+QPageSize PrinterCupsBackend::minimumPhysicalPageSize() const
2115+{
2116+ return m_info.minimumPhysicalPageSize();
2117+}
2118+
2119+QPageSize PrinterCupsBackend::maximumPhysicalPageSize() const
2120+{
2121+ return m_info.maximumPhysicalPageSize();
2122+}
2123+
2124+QList<int> PrinterCupsBackend::supportedResolutions() const
2125+{
2126+ return m_info.supportedResolutions();
2127+}
2128+
2129+PrinterEnum::DuplexMode PrinterCupsBackend::defaultDuplexMode() const
2130+{
2131+ return Utils::qDuplexModeToDuplexMode(m_info.defaultDuplexMode());
2132+}
2133+
2134+QList<PrinterEnum::DuplexMode> PrinterCupsBackend::supportedDuplexModes() const
2135+{
2136+ QList<PrinterEnum::DuplexMode> list;
2137+ Q_FOREACH(const QPrinter::DuplexMode mode, m_info.supportedDuplexModes()) {
2138+ if (mode != QPrinter::DuplexAuto) {
2139+ list.append(Utils::qDuplexModeToDuplexMode(mode));
2140+ }
2141+ }
2142+
2143+ if (list.isEmpty())
2144+ list.append(PrinterEnum::DuplexMode::DuplexNone);
2145+
2146+ return list;
2147+}
2148+
2149+QList<QSharedPointer<Printer>> PrinterCupsBackend::availablePrinters()
2150+{
2151+ return QList<QSharedPointer<Printer>>();
2152+}
2153+
2154+QStringList PrinterCupsBackend::availablePrinterNames()
2155+{
2156+ return QPrinterInfo::availablePrinterNames();
2157+}
2158+
2159+QSharedPointer<Printer> PrinterCupsBackend::getPrinter(const QString &printerName)
2160+{
2161+ QPrinterInfo info = QPrinterInfo::printerInfo(printerName);
2162+ return QSharedPointer<Printer>(new Printer(new PrinterCupsBackend(m_client, info, m_notifier)));
2163+}
2164+
2165+QString PrinterCupsBackend::defaultPrinterName()
2166+{
2167+ return QPrinterInfo::defaultPrinterName();
2168+}
2169+
2170+void PrinterCupsBackend::requestPrinter(const QString &printerName)
2171+{
2172+ if (m_activeRequests.contains(printerName)) {
2173+ return;
2174+ }
2175+
2176+ auto thread = new QThread;
2177+ auto loader = new PrinterLoader(printerName, m_client, m_notifier);
2178+ loader->moveToThread(thread);
2179+ connect(thread, SIGNAL(started()), loader, SLOT(load()));
2180+ connect(loader, SIGNAL(finished()), thread, SLOT(quit()));
2181+ connect(loader, SIGNAL(finished()), loader, SLOT(deleteLater()));
2182+ connect(loader, SIGNAL(loaded(QSharedPointer<Printer>)),
2183+ this, SIGNAL(printerLoaded(QSharedPointer<Printer>)));
2184+ connect(loader, SIGNAL(loaded(QSharedPointer<Printer>)),
2185+ this, SLOT(onPrinterLoaded(QSharedPointer<Printer>)));
2186+ connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
2187+ thread->start();
2188+
2189+ m_activeRequests << printerName;
2190+}
2191+
2192+void PrinterCupsBackend::requestPrinterDrivers()
2193+{
2194+ auto thread = new QThread;
2195+ auto loader = new PrinterDriverLoader();
2196+ loader->moveToThread(thread);
2197+ connect(loader, SIGNAL(error(const QString&)),
2198+ this, SIGNAL(printerDriversFailedToLoad(const QString&)));
2199+ connect(this, SIGNAL(requestPrinterDriverCancel()), loader, SLOT(cancel()));
2200+ connect(thread, SIGNAL(started()), loader, SLOT(process()));
2201+ connect(loader, SIGNAL(finished()), thread, SLOT(quit()));
2202+ connect(loader, SIGNAL(finished()), loader, SLOT(deleteLater()));
2203+ connect(loader, SIGNAL(loaded(const QList<PrinterDriver>&)),
2204+ this, SIGNAL(printerDriversLoaded(const QList<PrinterDriver>&)));
2205+ connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
2206+ thread->start();
2207+}
2208+
2209+void PrinterCupsBackend::cancelPrinterDriverRequest()
2210+{
2211+ Q_EMIT requestPrinterDriverCancel();
2212+}
2213+
2214+void PrinterCupsBackend::searchForDevices()
2215+{
2216+
2217+ auto thread = new QThread;
2218+ auto searcher = new DeviceSearcher();
2219+ searcher->moveToThread(thread);
2220+ connect(thread, SIGNAL(started()), searcher, SLOT(load()));
2221+ connect(searcher, SIGNAL(finished()), thread, SLOT(quit()));
2222+ connect(searcher, SIGNAL(finished()), searcher, SLOT(deleteLater()));
2223+ connect(searcher, SIGNAL(finished()), this, SIGNAL(deviceSearchFinished()));
2224+ connect(searcher, SIGNAL(loaded(const Device&)),
2225+ this, SIGNAL(deviceFound(const Device&)));
2226+ connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
2227+ thread->start();
2228+}
2229+
2230+void PrinterCupsBackend::refresh()
2231+{
2232+ if (m_printerName.isEmpty()) {
2233+ throw std::invalid_argument("Trying to refresh unnamed printer.");
2234+ } else {
2235+ m_info = QPrinterInfo::printerInfo(m_printerName);
2236+ }
2237+}
2238+
2239+void PrinterCupsBackend::createSubscription()
2240+{
2241+ m_cupsSubscriptionId = m_client->createSubscription();;
2242+}
2243+
2244+void PrinterCupsBackend::cancelSubscription()
2245+{
2246+ if (m_cupsSubscriptionId > 0)
2247+ m_client->cancelSubscription(m_cupsSubscriptionId);
2248+}
2249+
2250+QString PrinterCupsBackend::getPrinterInstance(const QString &name) const
2251+{
2252+ const auto parts = name.splitRef(QLatin1Char('/'));
2253+ QString instance;
2254+ if (parts.size() > 1)
2255+ instance = parts.at(1).toString();
2256+
2257+ return instance;
2258+}
2259+
2260+QString PrinterCupsBackend::getPrinterName(const QString &name) const
2261+{
2262+ return name.splitRef(QLatin1Char('/')).first().toString();
2263+}
2264+
2265+cups_dest_t* PrinterCupsBackend::getDest(const QString &name) const
2266+{
2267+ QString printerName = getPrinterName(name);
2268+ QString instance = getPrinterInstance(name);
2269+
2270+ if (m_dests.contains(name)) {
2271+ return m_dests[name];
2272+ } else {
2273+ m_dests[name] = m_client->getDest(printerName, instance);
2274+ return m_dests[name];
2275+ }
2276+}
2277+
2278+ppd_file_t* PrinterCupsBackend::getPpd(const QString &name) const
2279+{
2280+ QString printerName = getPrinterName(name);
2281+ QString instance = getPrinterInstance(name);
2282+
2283+ if (m_ppds.contains(name)) {
2284+ return m_ppds[name];
2285+ } else {
2286+ m_ppds[name] = m_client->getPpdFile(printerName, instance);
2287+ return m_ppds[name];
2288+ }
2289+}
2290+
2291+bool PrinterCupsBackend::isExtendedAttribute(const QString &attributeName) const
2292+{
2293+ return m_extendedAttributeNames.contains(attributeName);
2294+}
2295+
2296+void PrinterCupsBackend::onPrinterLoaded(QSharedPointer<Printer> printer)
2297+{
2298+ m_activeRequests.remove(printer->name());
2299+}
2300+
2301
2302=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h'
2303--- modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h 1970-01-01 00:00:00 +0000
2304+++ modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h 2017-03-13 15:55:01 +0000
2305@@ -0,0 +1,149 @@
2306+/*
2307+ * Copyright (C) 2017 Canonical, Ltd.
2308+ *
2309+ * This program is free software; you can redistribute it and/or modify
2310+ * it under the terms of the GNU Lesser General Public License as published by
2311+ * the Free Software Foundation; version 3.
2312+ *
2313+ * This program is distributed in the hope that it will be useful,
2314+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2315+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2316+ * GNU Lesser General Public License for more details.
2317+ *
2318+ * You should have received a copy of the GNU Lesser General Public License
2319+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2320+ */
2321+
2322+#ifndef USC_PRINTERS_CUPS_BACKEND_H
2323+#define USC_PRINTERS_CUPS_BACKEND_H
2324+
2325+#include "backend/backend.h"
2326+#include "cups/ippclient.h"
2327+#include "cupsdnotifier.h" // Note: this file was generated.
2328+
2329+#include <cups/cups.h>
2330+
2331+#include <QPrinterInfo>
2332+#include <QSet>
2333+
2334+class PRINTERS_DECL_EXPORT PrinterCupsBackend : public PrinterBackend
2335+{
2336+ Q_OBJECT
2337+public:
2338+ explicit PrinterCupsBackend(IppClient *client, QPrinterInfo info,
2339+ OrgCupsCupsdNotifierInterface* notifier,
2340+ QObject *parent = Q_NULLPTR);
2341+ virtual ~PrinterCupsBackend() override;
2342+
2343+ virtual bool holdsDefinition() const override;
2344+
2345+ virtual QString printerAdd(const QString &name,
2346+ const QString &uri,
2347+ const QString &ppdFile,
2348+ const QString &info,
2349+ const QString &location) override;
2350+ virtual QString printerAddWithPpd(const QString &name,
2351+ const QString &uri,
2352+ const QString &ppdFileName,
2353+ const QString &info,
2354+ const QString &location) override;
2355+ virtual QString printerDelete(const QString &name) override;
2356+ virtual QString printerSetDefault(const QString &name) override;
2357+ virtual QString printerSetEnabled(const QString &name,
2358+ const bool enabled) override;
2359+ virtual QString printerSetAcceptJobs(
2360+ const QString &name,
2361+ const bool accept,
2362+ const QString &reason = QString::null) override;
2363+ virtual QString printerSetShared(const QString &name,
2364+ const bool shared) override;
2365+ virtual QString printerSetInfo(const QString &name,
2366+ const QString &info) override;
2367+ virtual QString printerAddOption(const QString &name,
2368+ const QString &option,
2369+ const QStringList &values) override;
2370+
2371+ virtual QVariant printerGetOption(const QString &name,
2372+ const QString &option) const override;
2373+ virtual QMap<QString, QVariant> printerGetOptions(
2374+ const QString &name, const QStringList &options
2375+ ) const override;
2376+ // FIXME: maybe have a PrinterDest iface that has a CupsDest impl?
2377+ virtual cups_dest_t* makeDest(const QString &name,
2378+ const PrinterJob *options) override;
2379+
2380+ virtual void cancelJob(const QString &name, const int jobId) override;
2381+ virtual void holdJob(const QString &name, const int jobId) override;
2382+ virtual void releaseJob(const QString &name, const int jobId) override;
2383+ virtual int printFileToDest(const QString &filepath,
2384+ const QString &title,
2385+ const cups_dest_t *dest) override;
2386+ virtual QList<QSharedPointer<PrinterJob>> printerGetJobs() override;
2387+
2388+ virtual QString printerName() const override;
2389+ virtual QString description() const override;
2390+ virtual QString location() const override;
2391+ virtual QString makeAndModel() const override;
2392+ virtual bool isRemote() const override;
2393+
2394+ virtual PrinterEnum::State state() const override;
2395+ virtual QList<QPageSize> supportedPageSizes() const override;
2396+ virtual QPageSize defaultPageSize() const override;
2397+ virtual bool supportsCustomPageSizes() const override;
2398+
2399+ virtual QPageSize minimumPhysicalPageSize() const override;
2400+ virtual QPageSize maximumPhysicalPageSize() const override;
2401+ virtual QList<int> supportedResolutions() const override;
2402+ virtual PrinterEnum::DuplexMode defaultDuplexMode() const override;
2403+ virtual QList<PrinterEnum::DuplexMode> supportedDuplexModes() const override;
2404+
2405+ virtual QList<QSharedPointer<Printer>> availablePrinters() override;
2406+ virtual QStringList availablePrinterNames() override;
2407+ virtual QSharedPointer<Printer> getPrinter(const QString &printerName) override;
2408+ virtual QString defaultPrinterName() override;
2409+ virtual void requestPrinterDrivers() override;
2410+ virtual void requestPrinter(const QString &printerName) override;
2411+ virtual QMap<QString, QVariant> printerGetJobAttributes(
2412+ const QString &name, const int jobId) override;
2413+
2414+public Q_SLOTS:
2415+ virtual void refresh() override;
2416+ void createSubscription();
2417+
2418+ /* Starts a search for devices. Devices are synonymous with network devices
2419+ that may potentially be queues. This search will take at minimum 5 seconds,
2420+ normally longer. */
2421+ void searchForDevices();
2422+
2423+Q_SIGNALS:
2424+ void cancelWorkers();
2425+ void printerDriversLoaded(const QList<PrinterDriver> &drivers);
2426+ void printerDriversFailedToLoad(const QString &errorMessage);
2427+ void requestPrinterDriverCancel();
2428+
2429+private:
2430+ void cancelSubscription();
2431+ void cancelPrinterDriverRequest();
2432+ QList<cups_job_t *> getCupsJobs(const QString &name = QStringLiteral());
2433+
2434+ QString getPrinterName(const QString &name) const;
2435+ QString getPrinterInstance(const QString &name) const;
2436+ cups_dest_t* getDest(const QString &name) const;
2437+ ppd_file_t* getPpd(const QString &name) const;
2438+ bool isExtendedAttribute(const QString &attributeName) const;
2439+
2440+ const QStringList m_knownQualityOptions;
2441+ const QStringList m_extendedAttributeNames;
2442+ IppClient *m_client;
2443+ QPrinterInfo m_info;
2444+ OrgCupsCupsdNotifierInterface *m_notifier;
2445+ int m_cupsSubscriptionId;
2446+ mutable QMap<QString, cups_dest_t*> m_dests; // Printer name, dest.
2447+ mutable QMap<QString, ppd_file_t*> m_ppds; // Printer name, ppd.
2448+ QSet<QString> m_activeRequests;
2449+
2450+private Q_SLOTS:
2451+ void onPrinterLoaded(QSharedPointer<Printer> printer);
2452+};
2453+
2454+#endif // USC_PRINTERS_CUPS_BACKEND_H
2455
2456=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp'
2457--- modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp 1970-01-01 00:00:00 +0000
2458+++ modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp 2017-03-13 15:55:01 +0000
2459@@ -0,0 +1,127 @@
2460+/*
2461+ * Copyright (C) 2017 Canonical, Ltd.
2462+ *
2463+ * This program is free software; you can redistribute it and/or modify
2464+ * it under the terms of the GNU Lesser General Public License as published by
2465+ * the Free Software Foundation; version 3.
2466+ *
2467+ * This program is distributed in the hope that it will be useful,
2468+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2469+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2470+ * GNU Lesser General Public License for more details.
2471+ *
2472+ * You should have received a copy of the GNU Lesser General Public License
2473+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2474+ */
2475+
2476+#include "i18n.h"
2477+#include "backend/backend_pdf.h"
2478+
2479+PrinterPdfBackend::PrinterPdfBackend(const QString &printerName,
2480+ QObject *parent)
2481+ : PrinterBackend(printerName, parent)
2482+{
2483+ m_type = PrinterEnum::PrinterType::PdfType;
2484+}
2485+
2486+QVariant PrinterPdfBackend::printerGetOption(const QString &name,
2487+ const QString &option) const
2488+{
2489+ auto res = printerGetOptions(name, QStringList({option}));
2490+ return res[option];
2491+}
2492+
2493+QMap<QString, QVariant> PrinterPdfBackend::printerGetOptions(
2494+ const QString &name, const QStringList &options) const
2495+{
2496+ Q_UNUSED(name);
2497+
2498+ QMap<QString, QVariant> ret;
2499+
2500+ ColorModel rgb;
2501+ rgb.colorType = PrinterEnum::ColorModelType::ColorType;
2502+ rgb.name = "RGB";
2503+ rgb.text = __("Color");
2504+
2505+ PrintQuality quality;
2506+ quality.name = __("Normal");
2507+
2508+ Q_FOREACH(const QString &option, options) {
2509+ if (option == QLatin1String("DefaultColorModel")) {
2510+ ret[option] = QVariant::fromValue(rgb);
2511+ } else if (option == QLatin1String("DefaultPrintQuality")) {
2512+ ret[option] = QVariant::fromValue(quality);
2513+ } else if (option == QLatin1String("SupportedPrintQualities")) {
2514+ auto qualities = QList<PrintQuality>({quality});
2515+ ret[option] = QVariant::fromValue(qualities);
2516+ } else if (option == QLatin1String("SupportedColorModels")) {
2517+ auto models = QList<ColorModel>{rgb};
2518+ ret[option] = QVariant::fromValue(models);
2519+ } else if (option == QLatin1String("AcceptJobs")) {
2520+ ret[option] = true;
2521+ } else if (option == QLatin1String("StateReasons")) {
2522+ ret[option] = QLatin1String("");
2523+ } else if (option == QLatin1String("StateMessage")) {
2524+ ret[option] = QLatin1String("");
2525+ } else if (option == QLatin1String("DeviceUri")) {
2526+ ret[option] = QLatin1String("");
2527+ } else if (option == QLatin1String("Shared")) {
2528+ ret[option] = false;
2529+ } else {
2530+ throw std::invalid_argument("Invalid value for PDF printer: " + option.toStdString());
2531+ }
2532+ }
2533+
2534+ return ret;
2535+}
2536+
2537+QString PrinterPdfBackend::printerName() const
2538+{
2539+ return m_printerName;
2540+}
2541+
2542+PrinterEnum::State PrinterPdfBackend::state() const
2543+{
2544+ return PrinterEnum::State::IdleState;
2545+}
2546+
2547+QList<QPageSize> PrinterPdfBackend::supportedPageSizes() const
2548+{
2549+ return QList<QPageSize>{QPageSize(QPageSize::A4)};
2550+}
2551+
2552+QPageSize PrinterPdfBackend::defaultPageSize() const
2553+{
2554+ return QPageSize(QPageSize::A4);
2555+}
2556+
2557+bool PrinterPdfBackend::supportsCustomPageSizes() const
2558+{
2559+ return false;
2560+}
2561+
2562+QPageSize PrinterPdfBackend::minimumPhysicalPageSize() const
2563+{
2564+ return QPageSize(QPageSize::A4);
2565+}
2566+
2567+QPageSize PrinterPdfBackend::maximumPhysicalPageSize() const
2568+{
2569+ return QPageSize(QPageSize::A4);
2570+}
2571+
2572+QList<int> PrinterPdfBackend::supportedResolutions() const
2573+{
2574+ return QList<int>{};
2575+}
2576+
2577+PrinterEnum::DuplexMode PrinterPdfBackend::defaultDuplexMode() const
2578+{
2579+ return PrinterEnum::DuplexMode::DuplexNone;
2580+}
2581+
2582+QList<PrinterEnum::DuplexMode> PrinterPdfBackend::supportedDuplexModes() const
2583+{
2584+ return QList<PrinterEnum::DuplexMode>{PrinterEnum::DuplexMode::DuplexNone};
2585+}
2586+
2587
2588=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.h'
2589--- modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.h 1970-01-01 00:00:00 +0000
2590+++ modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.h 2017-03-13 15:55:01 +0000
2591@@ -0,0 +1,47 @@
2592+/*
2593+ * Copyright (C) 2017 Canonical, Ltd.
2594+ *
2595+ * This program is free software; you can redistribute it and/or modify
2596+ * it under the terms of the GNU Lesser General Public License as published by
2597+ * the Free Software Foundation; version 3.
2598+ *
2599+ * This program is distributed in the hope that it will be useful,
2600+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2601+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2602+ * GNU Lesser General Public License for more details.
2603+ *
2604+ * You should have received a copy of the GNU Lesser General Public License
2605+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2606+ */
2607+
2608+#ifndef USC_PRINTERS_PDF_BACKEND_H
2609+#define USC_PRINTERS_PDF_BACKEND_H
2610+
2611+#include "backend/backend.h"
2612+
2613+class PRINTERS_DECL_EXPORT PrinterPdfBackend : public PrinterBackend
2614+{
2615+public:
2616+ explicit PrinterPdfBackend(const QString &printerName,
2617+ QObject *parent = Q_NULLPTR);
2618+ virtual QVariant printerGetOption(const QString &name,
2619+ const QString &option) const override;
2620+ virtual QMap<QString, QVariant> printerGetOptions(
2621+ const QString &name, const QStringList &options
2622+ ) const override;
2623+
2624+ virtual QString printerName() const override;
2625+
2626+ virtual PrinterEnum::State state() const override;
2627+ virtual QList<QPageSize> supportedPageSizes() const override;
2628+ virtual QPageSize defaultPageSize() const override;
2629+ virtual bool supportsCustomPageSizes() const override;
2630+
2631+ virtual QPageSize minimumPhysicalPageSize() const override;
2632+ virtual QPageSize maximumPhysicalPageSize() const override;
2633+ virtual QList<int> supportedResolutions() const override;
2634+ virtual PrinterEnum::DuplexMode defaultDuplexMode() const override;
2635+ virtual QList<PrinterEnum::DuplexMode> supportedDuplexModes() const override;
2636+};
2637+
2638+#endif // USC_PRINTERS_PDF_BACKEND_H
2639
2640=== added directory 'modules/Ubuntu/Components/Extras/Printers/cups'
2641=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp'
2642--- modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp 1970-01-01 00:00:00 +0000
2643+++ modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp 2017-03-13 15:55:01 +0000
2644@@ -0,0 +1,70 @@
2645+/*
2646+ * Copyright (C) 2017 Canonical, Ltd.
2647+ *
2648+ * This program is free software; you can redistribute it and/or modify
2649+ * it under the terms of the GNU Lesser General Public License as published by
2650+ * the Free Software Foundation; version 3.
2651+ *
2652+ * This program is distributed in the hope that it will be useful,
2653+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2654+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2655+ * GNU Lesser General Public License for more details.
2656+ *
2657+ * You should have received a copy of the GNU Lesser General Public License
2658+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2659+ */
2660+
2661+#include "cups/ippclient.h"
2662+#include "devicesearcher.h"
2663+
2664+#include <QUrl>
2665+
2666+DeviceSearcher::DeviceSearcher(IppClient *client, QObject *parent)
2667+ : QObject(parent)
2668+ , m_client(client)
2669+{
2670+}
2671+
2672+DeviceSearcher::~DeviceSearcher()
2673+{
2674+ delete m_client;
2675+}
2676+
2677+void DeviceSearcher::load()
2678+{
2679+ if (!m_client->getDevices(&DeviceSearcher::deviceCallBack, this)) {
2680+ Q_EMIT failed(cupsLastErrorString());
2681+ }
2682+ Q_EMIT finished();
2683+}
2684+
2685+void DeviceSearcher::deviceCallBack(
2686+ const char *deviceClass,
2687+ const char *deviceId,
2688+ const char *deviceInfo,
2689+ const char *deviceMakeAndModel,
2690+ const char *deviceUri,
2691+ const char *deviceLocation,
2692+ void *context)
2693+{
2694+ auto searcher = qobject_cast<DeviceSearcher*>(static_cast<QObject*>(context));
2695+ if (!searcher) {
2696+ qWarning() << Q_FUNC_INFO << "context was not a DeviceSearcher.";
2697+ return;
2698+ }
2699+
2700+ Device d;
2701+ d.cls = deviceClass;
2702+ d.id = deviceId;
2703+ d.info = deviceInfo;
2704+ d.makeModel = deviceMakeAndModel;
2705+ d.uri = deviceUri;
2706+ d.location = deviceLocation;
2707+
2708+ searcher->deviceFound(d);
2709+}
2710+
2711+void DeviceSearcher::deviceFound(const Device &device)
2712+{
2713+ Q_EMIT loaded(device);
2714+}
2715
2716=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h'
2717--- modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h 1970-01-01 00:00:00 +0000
2718+++ modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h 2017-03-13 15:55:01 +0000
2719@@ -0,0 +1,57 @@
2720+/*
2721+ * Copyright (C) 2017 Canonical, Ltd.
2722+ *
2723+ * This program is free software; you can redistribute it and/or modify
2724+ * it under the terms of the GNU Lesser General Public License as published by
2725+ * the Free Software Foundation; version 3.
2726+ *
2727+ * This program is distributed in the hope that it will be useful,
2728+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2729+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2730+ * GNU Lesser General Public License for more details.
2731+ *
2732+ * You should have received a copy of the GNU Lesser General Public License
2733+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2734+ */
2735+
2736+#ifndef USC_PRINTERS_CUPS_DEVICESEARCHER_H
2737+#define USC_PRINTERS_CUPS_DEVICESEARCHER_H
2738+
2739+#include "cups/ippclient.h"
2740+#include "structs.h"
2741+
2742+#include <QObject>
2743+#include <QString>
2744+
2745+/* Class representing a device search. It is a worker object due to the time
2746+such a search takes (minimum 10 seconds). */
2747+class DeviceSearcher : public QObject
2748+{
2749+ Q_OBJECT
2750+ IppClient *m_client;
2751+public:
2752+ explicit DeviceSearcher(IppClient *client = new IppClient,
2753+ QObject *parent = Q_NULLPTR);
2754+ ~DeviceSearcher();
2755+
2756+public Q_SLOTS:
2757+ void load();
2758+
2759+private:
2760+ static void deviceCallBack(
2761+ const char *cls,
2762+ const char *id,
2763+ const char *info,
2764+ const char *makeAndModel,
2765+ const char *uri,
2766+ const char *location,
2767+ void *context);
2768+ void deviceFound(const Device &device);
2769+
2770+Q_SIGNALS:
2771+ void loaded(const Device &device);
2772+ void failed(const QString &errorMessage);
2773+ void finished();
2774+};
2775+
2776+#endif // USC_PRINTERS_CUPS_DEVICESEARCHER_H
2777
2778=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp'
2779--- modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp 1970-01-01 00:00:00 +0000
2780+++ modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp 2017-03-13 15:55:01 +0000
2781@@ -0,0 +1,1114 @@
2782+/*
2783+ * Copyright (C) 2017 Canonical, Ltd.
2784+ * Copyright (C) 2014 John Layt <jlayt@kde.org>
2785+ * Copyright (C) 2002, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014 Red Hat, Inc.
2786+ * Copyright (C) 2008 Novell, Inc.
2787+ *
2788+ * This program is free software; you can redistribute it and/or modify
2789+ * it under the terms of the GNU Lesser General Public License as published by
2790+ * the Free Software Foundation; version 3.
2791+ *
2792+ * This program is distributed in the hope that it will be useful,
2793+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2794+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2795+ * GNU Lesser General Public License for more details.
2796+ *
2797+ * You should have received a copy of the GNU Lesser General Public License
2798+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2799+ */
2800+
2801+#include "cups/ippclient.h"
2802+
2803+#include <errno.h>
2804+#include <string.h>
2805+#include <unistd.h>
2806+
2807+#include <QDebug>
2808+#include <QDateTime>
2809+#include <QtAlgorithms>
2810+#include <QTimeZone>
2811+#include <QUrl>
2812+
2813+IppClient::IppClient()
2814+ : m_connection(httpConnectEncrypt(cupsServer(),
2815+ ippPort(),
2816+ cupsEncryption()))
2817+{
2818+ if (!m_connection) {
2819+ qCritical("Failed to connect to cupsd");
2820+ } else {
2821+ qDebug("Successfully connected to cupsd.");
2822+ }
2823+}
2824+
2825+IppClient::~IppClient()
2826+{
2827+ if (m_connection)
2828+ httpClose(m_connection);
2829+}
2830+
2831+bool IppClient::printerDelete(const QString &printerName)
2832+{
2833+ return sendNewSimpleRequest(CUPS_DELETE_PRINTER, printerName.toUtf8(),
2834+ CupsResource::CupsResourceAdmin);
2835+}
2836+
2837+bool IppClient::printerAdd(const QString &printerName,
2838+ const QString &printerUri,
2839+ const QString &ppdFile,
2840+ const QString &info,
2841+ const QString &location)
2842+{
2843+ ipp_t *request;
2844+
2845+ if (!isPrinterNameValid(printerName)) {
2846+ setInternalStatus(QString("%1 is not a valid printer name.").arg(printerName));
2847+ return false;
2848+ }
2849+
2850+ if (!isStringValid(info)) {
2851+ setInternalStatus(QString("%1 is not a valid description.").arg(info));
2852+ return false;
2853+ }
2854+
2855+ if (!isStringValid(location)) {
2856+ setInternalStatus(QString("%1 is not a valid location.").arg(location));
2857+ return false;
2858+ }
2859+
2860+ if (!isStringValid(ppdFile)) {
2861+ setInternalStatus(QString("%1 is not a valid ppd file.").arg(ppdFile));
2862+ return false;
2863+ }
2864+
2865+ if (!isStringValid(printerUri)) {
2866+ setInternalStatus(QString("%1 is not a valid printer uri.").arg(printerUri));
2867+ return false;
2868+ }
2869+
2870+
2871+ request = ippNewRequest (CUPS_ADD_MODIFY_PRINTER);
2872+ addPrinterUri(request, printerName);
2873+ addRequestingUsername(request, NULL);
2874+
2875+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2876+ "printer-name", NULL, printerName.toUtf8());
2877+
2878+ if (!ppdFile.isEmpty()) {
2879+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2880+ "ppd-name", NULL, ppdFile.toUtf8());
2881+ }
2882+ if (!printerUri.isEmpty()) {
2883+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI,
2884+ "device-uri", NULL, printerUri.toUtf8());
2885+ }
2886+ if (!info.isEmpty()) {
2887+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2888+ "printer-info", NULL, info.toUtf8());
2889+ }
2890+ if (!location.isEmpty()) {
2891+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2892+ "printer-location", NULL, location.toUtf8());
2893+ }
2894+
2895+ return sendRequest(request, CupsResourceAdmin);
2896+}
2897+
2898+bool IppClient::printerAddWithPpdFile(const QString &printerName,
2899+ const QString &printerUri,
2900+ const QString &ppdFileName,
2901+ const QString &info,
2902+ const QString &location)
2903+{
2904+ ipp_t *request;
2905+
2906+ if (!isPrinterNameValid(printerName)) {
2907+ setInternalStatus(QString("%1 is not a valid printer name.").arg(printerName));
2908+ return false;
2909+ }
2910+
2911+ if (!isStringValid(info)) {
2912+ setInternalStatus(QString("%1 is not a valid description.").arg(info));
2913+ return false;
2914+ }
2915+
2916+ if (!isStringValid(location)) {
2917+ setInternalStatus(QString("%1 is not a valid location.").arg(location));
2918+ return false;
2919+ }
2920+
2921+ if (!isStringValid(ppdFileName)) {
2922+ setInternalStatus(QString("%1 is not a valid ppd file name.").arg(ppdFileName));
2923+ return false;
2924+ }
2925+
2926+ if (!isStringValid(printerUri)) {
2927+ setInternalStatus(QString("%1 is not a valid printer uri.").arg(printerUri));
2928+ return false;
2929+ }
2930+
2931+ request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
2932+ addPrinterUri(request, printerName);
2933+ addRequestingUsername(request, NULL);
2934+
2935+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2936+ "printer-name", NULL, printerName.toUtf8());
2937+
2938+ /* In this specific case of ADD_MODIFY, the URI can be NULL/empty since
2939+ * we provide a complete PPD. And cups fails if we pass an empty
2940+ * string. */
2941+ if (!printerUri.isEmpty()) {
2942+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI,
2943+ "device-uri", NULL, printerUri.toUtf8());
2944+ }
2945+
2946+ if (!info.isEmpty()) {
2947+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2948+ "printer-info", NULL, info.toUtf8());
2949+ }
2950+ if (!location.isEmpty()) {
2951+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2952+ "printer-location", NULL, location.toUtf8());
2953+ }
2954+
2955+ return postRequest(request, ppdFileName.toUtf8(), CupsResourceAdmin);
2956+}
2957+
2958+bool IppClient::printerHoldJob(const QString &printerName, const int jobId)
2959+{
2960+ ipp_t *request = ippNewRequest(IPP_HOLD_JOB);
2961+ addPrinterUri(request, printerName);
2962+ addRequestingUsername(request, NULL);
2963+
2964+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
2965+ "job-id", jobId);
2966+
2967+ return sendRequest(request, CupsResourceJobs);
2968+}
2969+
2970+bool IppClient::printerReleaseJob(const QString &printerName, const int jobId)
2971+{
2972+ ipp_t *request = ippNewRequest(IPP_RELEASE_JOB);
2973+ addPrinterUri(request, printerName);
2974+ addRequestingUsername(request, NULL);
2975+
2976+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
2977+ "job-id", jobId);
2978+
2979+ return sendRequest(request, CupsResourceJobs);
2980+}
2981+
2982+bool IppClient::printerSetDefault(const QString &printerName)
2983+{
2984+ return sendNewSimpleRequest(CUPS_SET_DEFAULT, printerName.toUtf8(),
2985+ CupsResource::CupsResourceAdmin);
2986+}
2987+
2988+bool IppClient::printerSetEnabled(const QString &printerName,
2989+ const bool enabled)
2990+{
2991+ ipp_op_t op;
2992+ op = enabled ? IPP_RESUME_PRINTER : IPP_PAUSE_PRINTER;
2993+ return sendNewSimpleRequest(op, printerName, CupsResourceAdmin);
2994+}
2995+
2996+/* reason must be empty if accept is true */
2997+bool IppClient::printerSetAcceptJobs(const QString &printerName,
2998+ const bool accept,
2999+ const QString &reason)
3000+{
3001+ ipp_t *request;
3002+
3003+ if (accept && !reason.isEmpty()) {
3004+ setInternalStatus("Accepting jobs does not take a reason.");
3005+ return false;
3006+ }
3007+
3008+ if (!isPrinterNameValid(printerName)) {
3009+ setInternalStatus(QString("%1 is not a valid printer name.").arg(printerName));
3010+ return false;
3011+ }
3012+
3013+ if (!isStringValid(reason)) {
3014+ setInternalStatus(QString("%1 is not a valid reason.").arg(reason));
3015+ return false;
3016+ }
3017+
3018+ if (accept) {
3019+ return sendNewSimpleRequest(CUPS_ACCEPT_JOBS, printerName.toUtf8(),
3020+ CupsResourceAdmin);
3021+ } else {
3022+ request = ippNewRequest(CUPS_REJECT_JOBS);
3023+ addPrinterUri(request, printerName);
3024+ addRequestingUsername(request, NULL);
3025+
3026+ if (!reason.isEmpty())
3027+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
3028+ "printer-state-message", NULL, reason.toUtf8());
3029+
3030+ return sendRequest(request, CupsResourceAdmin);
3031+ }
3032+}
3033+
3034+bool IppClient::printerSetShared(const QString &printerName, const bool shared)
3035+{
3036+ ipp_t *request;
3037+
3038+ if (!isPrinterNameValid(printerName)) {
3039+ setInternalStatus(QString("%1 is not a valid printer name.").arg(printerName));
3040+ return false;
3041+ }
3042+
3043+ request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
3044+ addPrinterUri(request, printerName);
3045+ addRequestingUsername(request, NULL);
3046+ ippAddBoolean(request, IPP_TAG_OPERATION,
3047+ "printer-is-shared", shared ? 1 : 0);
3048+
3049+ /* TODO: The request will fail if this was a printer class, and it should
3050+ be retried. */
3051+
3052+ return sendRequest(request, CupsResourceAdmin);
3053+}
3054+
3055+bool IppClient::printerClassSetInfo(const QString &name,
3056+ const QString &info)
3057+{
3058+ if (!isPrinterNameValid(name)) {
3059+ setInternalStatus(QString("%1 is not a valid printer name.").arg(name));
3060+ return false;
3061+ }
3062+
3063+ if (!isStringValid(info)) {
3064+ setInternalStatus(QString("%1 is not a valid description.").arg(info));
3065+ return false;
3066+ }
3067+
3068+ return sendNewPrinterClassRequest(name, IPP_TAG_PRINTER, IPP_TAG_TEXT,
3069+ "printer-info", info);
3070+}
3071+
3072+bool IppClient::printerClassSetOption(const QString &name,
3073+ const QString &option,
3074+ const QStringList &values)
3075+{
3076+ bool isClass;
3077+ int length = 0;
3078+ ipp_t *request;
3079+ ipp_attribute_t *attr;
3080+ QString newPpdFile;
3081+ bool retval;
3082+
3083+ if (!isPrinterNameValid(name)) {
3084+ setInternalStatus(QString("%1 is not a valid printer name.").arg(name));
3085+ return false;
3086+ }
3087+
3088+ if (!isStringValid(option)) {
3089+ setInternalStatus(QString("%1 is not a valid option.").arg(option));
3090+ return false;
3091+ }
3092+
3093+ Q_FOREACH(const QString &val, values) {
3094+ if (!isStringValid(val)) {
3095+ setInternalStatus(QString("%1 is not a valid value.").arg(val));
3096+ return false;
3097+ }
3098+ length++;
3099+ }
3100+
3101+ if (length == 0) {
3102+ setInternalStatus("No valid values.");
3103+ return false;
3104+ }
3105+
3106+ isClass = printerIsClass(name);
3107+
3108+ /* We permit only one value to change in PPD file because we are setting
3109+ * default value in it. */
3110+ if (!isClass && length == 1) {
3111+ cups_option_t *options = NULL;
3112+ int numOptions = 0;
3113+ QString ppdfile;
3114+
3115+ numOptions = cupsAddOption(option.toUtf8(),
3116+ values[0].toUtf8(),
3117+ numOptions, &options);
3118+
3119+ ppdfile = QString(cupsGetPPD(name.toUtf8()));
3120+
3121+ newPpdFile = preparePpdForOptions(ppdfile.toUtf8(),
3122+ options, numOptions).toLatin1().data();
3123+
3124+ unlink(ppdfile.toUtf8());
3125+ cupsFreeOptions(numOptions, options);
3126+ }
3127+
3128+ if (isClass) {
3129+ request = ippNewRequest(CUPS_ADD_MODIFY_CLASS);
3130+ addClassUri(request, name);
3131+ } else {
3132+ request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
3133+ addPrinterUri(request, name);
3134+ }
3135+
3136+ addRequestingUsername(request, NULL);
3137+
3138+ if (length == 1) {
3139+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3140+ option.toUtf8(),
3141+ NULL,
3142+ values[0].toUtf8());
3143+ } else {
3144+ int i;
3145+
3146+ attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3147+ option.toUtf8(), length, NULL, NULL);
3148+
3149+ for (i = 0; i < length; i++)
3150+ ippSetString(request, &attr, i, values[i].toUtf8());
3151+ }
3152+
3153+ if (!newPpdFile.isEmpty()) {
3154+ retval = postRequest(request, newPpdFile, CupsResourceAdmin);
3155+
3156+ unlink(newPpdFile.toUtf8());
3157+ // TODO: fix leak here.
3158+ } else {
3159+ retval = sendRequest(request, CupsResourceAdmin);
3160+ }
3161+
3162+ return retval;
3163+}
3164+
3165+QMap<QString, QVariant> IppClient::printerGetAttributes(
3166+ const QString &printerName, const QStringList &attributes)
3167+{
3168+ QMap<QString, QVariant> result;
3169+ QList<QByteArray*> attrByteArrays;
3170+ ipp_t *request;
3171+ ipp_attribute_t *attr;
3172+
3173+ if (attributes.isEmpty()) {
3174+ return result;
3175+ }
3176+
3177+ int i;
3178+ size_t n = attributes.size();
3179+ char **attrs;
3180+ attrs = (char**) malloc ((n + 1) * sizeof (char *));
3181+
3182+ /* This is some trickery to compensate for a lack of C array/pointer
3183+ skills. The VLA attrs gets the underlying data structure of a QByterArray
3184+ on the heap. When cups is done with it, and in the end of this method,
3185+ the QByteArrays are deleted, and attrs is freed. */
3186+ for (i = 0; i < ((int) n); i++) {
3187+ QByteArray *array = new QByteArray(attributes.value(i).toLocal8Bit());
3188+ attrByteArrays << array;
3189+ attrs[i] = array->data();
3190+ }
3191+ attrs[n] = NULL;
3192+
3193+ request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
3194+ addPrinterUri(request, printerName);
3195+ addRequestingUsername(request, QString());
3196+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
3197+ "requested-attributes", attributes.size(), NULL, attrs);
3198+ auto resource = getResource(CupsResource::CupsResourceRoot);
3199+
3200+ ipp_t *reply;
3201+ reply = cupsDoRequest(m_connection, request, resource.toUtf8());
3202+
3203+ if (!isReplyOk(reply, false)) {
3204+ qWarning() << Q_FUNC_INFO << "failed to get attributes"
3205+ << attributes << "for printer" << printerName;
3206+ } else {
3207+ for (attr = ippFirstAttribute(reply); attr; attr = ippNextAttribute(reply)) {
3208+ while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
3209+ attr = ippNextAttribute(reply);
3210+
3211+ if (!attr)
3212+ break;
3213+
3214+ for (; attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER;
3215+ attr = ippNextAttribute(reply)) {
3216+
3217+ /* TODO: Here we need to take into account that the returned
3218+ attributes could be lists and other things. */
3219+ result[ippGetName(attr)] = getAttributeValue(attr);
3220+
3221+ if (!attr)
3222+ break;
3223+ }
3224+ }
3225+ }
3226+
3227+ if (reply)
3228+ ippDelete(reply);
3229+
3230+ qDeleteAll(attrByteArrays);
3231+ free(attrs);
3232+
3233+ return result;
3234+}
3235+
3236+QMap<QString, QVariant> IppClient::printerGetJobAttributes(const QString &printerName,
3237+ const int jobId)
3238+{
3239+ ipp_t *request;
3240+ QMap<QString, QVariant> map;
3241+
3242+ // Construct request
3243+ request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
3244+
3245+ addPrinterUri(request, printerName);
3246+ addRequestingUsername(request, NULL);
3247+
3248+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
3249+ "job-id", jobId);
3250+
3251+ // Send request and construct reply
3252+ ipp_t *reply;
3253+ const QString resourceChar = getResource(CupsResourceRoot);
3254+ reply = cupsDoRequest(m_connection, request,
3255+ resourceChar.toUtf8());
3256+
3257+ // Check if the reply is OK
3258+ if (isReplyOk(reply, false)) {
3259+ // Loop through the attributes
3260+ ipp_attribute_t *attr;
3261+
3262+ for (attr = ippFirstAttribute(reply); attr; attr = ippNextAttribute(reply)) {
3263+ QVariant value = getAttributeValue(attr);
3264+ map.insert(ippGetName(attr), value);
3265+ }
3266+ } else {
3267+ qWarning() << "Not able to get attributes of job:" << jobId;
3268+ }
3269+
3270+ // Destruct the reply if valid
3271+ if (reply) {
3272+ ippDelete(reply);
3273+ }
3274+
3275+ return map;
3276+}
3277+
3278+
3279+/* This function sets given options to specified values in file 'ppdfile'.
3280+ * This needs to be done because of applications which use content of PPD files
3281+ * instead of IPP attributes.
3282+ * CUPS doesn't do this automatically (but hopefully will starting with 1.6) */
3283+QString IppClient::preparePpdForOptions(const QString &ppdfile,
3284+ cups_option_t *options, int numOptions)
3285+{
3286+ auto ppdfile_c = ppdfile.toUtf8();
3287+ ppd_file_t *ppd;
3288+ bool ppdchanged = false;
3289+ QString result;
3290+ QString error;
3291+ char newppdfile[PATH_MAX];
3292+ cups_file_t *in = NULL;
3293+ cups_file_t *out = NULL;
3294+ char line[CPH_STR_MAXLEN];
3295+ char keyword[CPH_STR_MAXLEN];
3296+ char *keyptr;
3297+ ppd_choice_t *choice;
3298+ QString value;
3299+ QLatin1String defaultStr("*Default");
3300+
3301+ ppd = ppdOpenFile(ppdfile_c);
3302+ if (!ppd) {
3303+ error = QString("Unable to open PPD file \"%1\": %2")
3304+ .arg(ppdfile).arg(strerror(errno));
3305+ setInternalStatus(error);
3306+ goto out;
3307+ }
3308+
3309+ in = cupsFileOpen(ppdfile_c, "r");
3310+ if (!in) {
3311+ error = QString("Unable to open PPD file \"%1\": %2")
3312+ .arg(ppdfile).arg(strerror(errno));
3313+ setInternalStatus(error);
3314+ goto out;
3315+ }
3316+
3317+ out = cupsTempFile2(newppdfile, sizeof(newppdfile));
3318+ if (!out) {
3319+ setInternalStatus("Unable to create temporary file");
3320+ goto out;
3321+ }
3322+
3323+ /* Mark default values and values of options we are changing. */
3324+ ppdMarkDefaults(ppd);
3325+ cupsMarkOptions(ppd, numOptions, options);
3326+
3327+ while (cupsFileGets(in, line, sizeof(line))) {
3328+ QString line_qs(line);
3329+ if (!line_qs.startsWith(defaultStr)) {
3330+ cupsFilePrintf(out, "%s\n", line);
3331+ } else {
3332+ /* This part parses lines with *Default on their
3333+ * beginning. For instance:
3334+ * "*DefaultResolution: 1200dpi" becomes:
3335+ * - keyword: Resolution
3336+ * - keyptr: 1200dpi
3337+ */
3338+ strncpy(keyword, line + defaultStr.size(), sizeof(keyword));
3339+
3340+ for (keyptr = keyword; *keyptr; keyptr++)
3341+ if (*keyptr == ':' || isspace (*keyptr & 255))
3342+ break;
3343+
3344+ *keyptr++ = '\0';
3345+ while (isspace (*keyptr & 255))
3346+ keyptr++;
3347+
3348+ QString keyword_sq(keyword);
3349+ QString keyptr_qs(keyptr);
3350+
3351+ /* We have to change PageSize if any of PageRegion,
3352+ * PageSize, PaperDimension or ImageableArea changes.
3353+ * We change PageRegion if PageSize is not available. */
3354+ if (keyword_sq == "PageRegion" ||
3355+ keyword_sq == "PageSize" ||
3356+ keyword_sq == "PaperDimension" ||
3357+ keyword_sq == "ImageableArea") {
3358+
3359+ choice = ppdFindMarkedChoice(ppd, "PageSize");
3360+ if (!choice)
3361+ choice = ppdFindMarkedChoice(ppd, "PageRegion");
3362+ } else {
3363+ choice = ppdFindMarkedChoice(ppd, keyword);
3364+ }
3365+
3366+
3367+ QString choice_qs;
3368+ if (choice) {
3369+ choice_qs = choice->choice;
3370+ }
3371+
3372+ if (choice && choice_qs != keyptr_qs) {
3373+ /* We have to set the value in PPD manually if
3374+ * a custom value was passed in:
3375+ * cupsMarkOptions() marks the choice as
3376+ * "Custom". We want to set this value with our
3377+ * input. */
3378+ if (choice_qs != "Custom") {
3379+ cupsFilePrintf(out,
3380+ "*Default%s: %s\n",
3381+ keyword,
3382+ choice->choice);
3383+ ppdchanged = true;
3384+ } else {
3385+ value = cupsGetOption(keyword, numOptions, options);
3386+ if (!value.isEmpty()) {
3387+ cupsFilePrintf(out,
3388+ "*Default%s: %s\n",
3389+ keyword,
3390+ value.toStdString().c_str());
3391+ ppdchanged = true;
3392+ } else {
3393+ cupsFilePrintf(out, "%s\n", line);
3394+ }
3395+ }
3396+ } else {
3397+ cupsFilePrintf(out, "%s\n", line);
3398+ }
3399+ }
3400+ }
3401+
3402+ if (ppdchanged)
3403+ result = QString::fromUtf8(newppdfile);
3404+ else
3405+ unlink(newppdfile);
3406+
3407+out:
3408+ if (in)
3409+ cupsFileClose(in);
3410+ if (out)
3411+ cupsFileClose(out);
3412+ if (ppd)
3413+ ppdClose(ppd);
3414+
3415+ return result;
3416+}
3417+
3418+
3419+bool IppClient::sendNewPrinterClassRequest(const QString &printerName,
3420+ ipp_tag_t group, ipp_tag_t type,
3421+ const QString &name,
3422+ const QString &value)
3423+{
3424+ ipp_t *request;
3425+
3426+ request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
3427+ addPrinterUri(request, printerName);
3428+ addRequestingUsername(request, QString());
3429+ ippAddString(request, group, type, name.toUtf8(), NULL,
3430+ value.toUtf8());
3431+
3432+ if (sendRequest(request, CupsResource::CupsResourceAdmin))
3433+ return true;
3434+
3435+ // it failed, maybe it was a class?
3436+ if (m_lastStatus != IPP_NOT_POSSIBLE) {
3437+ return false;
3438+ }
3439+
3440+ // TODO: implement class modification <here>.
3441+ return false;
3442+}
3443+
3444+void IppClient::addPrinterUri(ipp_t *request, const QString &name)
3445+{
3446+ QUrl uri(QString("ipp://localhost/printers/%1").arg(name));
3447+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
3448+ "printer-uri", NULL, uri.toEncoded().data());
3449+}
3450+
3451+void IppClient::addRequestingUsername(ipp_t *request, const QString &username)
3452+{
3453+ if (!username.isEmpty())
3454+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
3455+ "requesting-user-name", NULL,
3456+ username.toUtf8());
3457+ else
3458+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
3459+ "requesting-user-name", NULL, cupsUser());
3460+}
3461+
3462+QString IppClient::getLastError() const
3463+{
3464+ return m_internalStatus;
3465+}
3466+
3467+const QString IppClient::getResource(const IppClient::CupsResource &resource)
3468+{
3469+ switch (resource) {
3470+ case CupsResourceRoot:
3471+ return "/";
3472+ case CupsResourceAdmin:
3473+ return "/admin/";
3474+ case CupsResourceJobs:
3475+ return "/jobs/";
3476+ default:
3477+ qCritical("Asking for a resource with no match.");
3478+ return "/";
3479+ }
3480+}
3481+
3482+bool IppClient::isPrinterNameValid(const QString &name)
3483+{
3484+ int i;
3485+ int len;
3486+
3487+ /* Quoting the lpadmin man page:
3488+ * CUPS allows printer names to contain any printable character
3489+ * except SPACE, TAB, "/", or "#".
3490+ * On top of that, validate_name() in lpadmin.c (from cups) checks that
3491+ * the string is 127 characters long, or shorter. */
3492+
3493+ /* no empty string */
3494+ if (name.isEmpty())
3495+ return false;
3496+
3497+ len = name.size();
3498+ /* no string that is too long; see comment at the beginning of the
3499+ * validation code block */
3500+ if (len > 127)
3501+ return false;
3502+
3503+ /* only printable characters, no space, no /, no # */
3504+ for (i = 0; i < len; i++) {
3505+ const QChar c = name.at(i);
3506+ if (!c.isPrint())
3507+ return false;
3508+ if (c.isSpace())
3509+ return false;
3510+ if (c == '/' || c == '#')
3511+ return false;
3512+ }
3513+ return true;
3514+}
3515+
3516+bool IppClient::isStringValid(const QString &string, const bool checkNull,
3517+ const int maxLength)
3518+{
3519+ if (isStringPrintable(string, checkNull, maxLength))
3520+ return true;
3521+ return false;
3522+}
3523+
3524+bool IppClient::isStringPrintable(const QString &string, const bool checkNull,
3525+ const int maxLength)
3526+{
3527+ int i;
3528+ int len;
3529+
3530+ /* no null string */
3531+ if (string.isNull())
3532+ return !checkNull;
3533+
3534+ len = string.size();
3535+ if (maxLength > 0 && len > maxLength)
3536+ return false;
3537+
3538+ /* only printable characters */
3539+ for (i = 0; i < len; i++) {
3540+ const QChar c = string.at(i);
3541+ if (!c.isPrint())
3542+ return false;
3543+ }
3544+ return true;
3545+}
3546+
3547+void IppClient::setInternalStatus(const QString &status)
3548+{
3549+ if (!m_internalStatus.isNull()) {
3550+ m_internalStatus = QString::null;
3551+ }
3552+
3553+ if (status.isNull()) {
3554+ m_internalStatus = QString::null;
3555+ } else {
3556+ m_internalStatus = status;
3557+
3558+ // Only used for errors for now.
3559+ qCritical() << status;
3560+ }
3561+}
3562+
3563+bool IppClient::postRequest(ipp_t *request, const QString &file,
3564+ const CupsResource &resource)
3565+{
3566+ ipp_t *reply;
3567+ QString resourceChar;
3568+
3569+ resourceChar = getResource(resource);
3570+
3571+ if (!file.isEmpty())
3572+ reply = cupsDoFileRequest(m_connection, request, resourceChar.toUtf8(),
3573+ file.toUtf8());
3574+ else
3575+ reply = cupsDoFileRequest(m_connection, request, resourceChar.toUtf8(),
3576+ NULL);
3577+
3578+ return handleReply(reply);
3579+}
3580+
3581+
3582+bool IppClient::sendRequest(ipp_t *request, const CupsResource &resource)
3583+{
3584+ ipp_t *reply;
3585+ const QString resourceChar = getResource(resource);
3586+ reply = cupsDoRequest(m_connection, request,
3587+ resourceChar.toUtf8());
3588+ return handleReply(reply);
3589+}
3590+
3591+bool IppClient::sendNewSimpleRequest(ipp_op_t op, const QString &printerName,
3592+ const IppClient::CupsResource &resource)
3593+{
3594+ ipp_t *request;
3595+
3596+ if (!isPrinterNameValid(printerName))
3597+ return false;
3598+
3599+ request = ippNewRequest(op);
3600+ addPrinterUri(request, printerName);
3601+ addRequestingUsername(request, NULL);
3602+
3603+ return sendRequest(request, resource);
3604+}
3605+
3606+bool IppClient::handleReply(ipp_t *reply)
3607+{
3608+ bool retval;
3609+ retval = isReplyOk(reply, false);
3610+ if (reply)
3611+ ippDelete(reply);
3612+
3613+ return retval;
3614+}
3615+
3616+bool IppClient::isReplyOk(ipp_t *reply, bool deleteIfReplyNotOk)
3617+{
3618+ /* reset the internal status: we'll use the cups status */
3619+ m_lastStatus = IPP_STATUS_CUPS_INVALID;
3620+
3621+ if (reply && ippGetStatusCode(reply) <= IPP_OK_CONFLICT) {
3622+ m_lastStatus = IPP_OK;
3623+ return true;
3624+ } else {
3625+ setErrorFromReply(reply);
3626+ qWarning() << Q_FUNC_INFO << "Cups HTTP error:" << cupsLastErrorString();
3627+
3628+ if (deleteIfReplyNotOk && reply)
3629+ ippDelete(reply);
3630+
3631+ return false;
3632+ }
3633+}
3634+
3635+void IppClient::setErrorFromReply(ipp_t *reply)
3636+{
3637+ if (reply)
3638+ m_lastStatus = ippGetStatusCode(reply);
3639+ else
3640+ m_lastStatus = cupsLastError();
3641+}
3642+
3643+bool IppClient::printerIsClass(const QString &name)
3644+{
3645+ const char * const attrs[1] = { "member-names" };
3646+ ipp_t *request;
3647+ QString resource;
3648+ ipp_t *reply;
3649+ bool retval;
3650+
3651+ // Class/Printer name validation is equal.
3652+ if (!isPrinterNameValid(name)) {
3653+ setInternalStatus(QString("%1 is not a valid printer name.").arg(name));
3654+ return false;
3655+ }
3656+
3657+ request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
3658+ addClassUri(request, name);
3659+ addRequestingUsername(request, QString());
3660+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
3661+ "requested-attributes", 1, NULL, attrs);
3662+
3663+ resource = getResource(CupsResource::CupsResourceRoot);
3664+ reply = cupsDoRequest(m_connection, request, resource.toUtf8());
3665+
3666+ if (!isReplyOk(reply, true))
3667+ return true;
3668+
3669+ /* Note: we need to look if the attribute is there, since we get a
3670+ * reply if the name is a printer name and not a class name. The
3671+ * attribute is the only way to distinguish the two cases. */
3672+ retval = ippFindAttribute(reply, attrs[0], IPP_TAG_NAME) != NULL;
3673+
3674+ if (reply)
3675+ ippDelete(reply);
3676+
3677+ return retval;
3678+}
3679+
3680+void IppClient::addClassUri(ipp_t *request, const QString &name)
3681+{
3682+ QUrl uri(QString("ipp://localhost/printers/%1").arg(name));
3683+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
3684+ "printer-uri", NULL, uri.toEncoded().data());
3685+}
3686+
3687+ppd_file_t* IppClient::getPpdFile(const QString &name,
3688+ const QString &instance) const
3689+{
3690+ Q_UNUSED(instance);
3691+
3692+ ppd_file_t* file = 0;
3693+ const char *ppdFile = cupsGetPPD(name.toUtf8());
3694+ if (ppdFile) {
3695+ file = ppdOpenFile(ppdFile);
3696+ unlink(ppdFile);
3697+ }
3698+ if (file) {
3699+ ppdMarkDefaults(file);
3700+ } else {
3701+ file = 0;
3702+ }
3703+
3704+ return file;
3705+}
3706+
3707+cups_dest_t* IppClient::getDest(const QString &name,
3708+ const QString &instance) const
3709+{
3710+ cups_dest_t *dest = 0;
3711+
3712+ if (instance.isEmpty()) {
3713+ dest = cupsGetNamedDest(m_connection, name.toUtf8(), NULL);
3714+ } else {
3715+ dest = cupsGetNamedDest(m_connection, name.toUtf8(), instance.toUtf8());
3716+ }
3717+ return dest;
3718+}
3719+
3720+ipp_t* IppClient::createPrinterDriversRequest(
3721+ const QString &deviceId, const QString &language, const QString &makeModel,
3722+ const QString &product, const QStringList &includeSchemes,
3723+ const QStringList &excludeSchemes
3724+)
3725+{
3726+ Q_UNUSED(includeSchemes);
3727+ Q_UNUSED(excludeSchemes);
3728+
3729+ ipp_t *request;
3730+
3731+ request = ippNewRequest(CUPS_GET_PPDS);
3732+
3733+ if (!deviceId.isEmpty())
3734+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-device-id",
3735+ NULL, deviceId.toUtf8());
3736+ if (!language.isEmpty())
3737+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "ppd-language",
3738+ NULL, language.toUtf8());
3739+ if (!makeModel.isEmpty())
3740+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-make-and-model",
3741+ NULL, makeModel.toUtf8());
3742+ if (!product.isEmpty())
3743+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-product",
3744+ NULL, product.toUtf8());
3745+
3746+ // Do the request and get return the response.
3747+ const QString resourceChar = getResource(CupsResourceRoot);
3748+ return cupsDoRequest(m_connection, request,
3749+ resourceChar.toUtf8());
3750+}
3751+
3752+int IppClient::createSubscription()
3753+{
3754+ ipp_t *req;
3755+ ipp_t *resp;
3756+ ipp_attribute_t *attr;
3757+ int subscriptionId = -1;
3758+
3759+ req = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION);
3760+ ippAddString(req, IPP_TAG_OPERATION, IPP_TAG_URI,
3761+ "printer-uri", NULL, "/");
3762+ ippAddString(req, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
3763+ "notify-events", NULL, "all");
3764+ ippAddString(req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
3765+ "notify-recipient-uri", NULL, "dbus://");
3766+ ippAddInteger(req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
3767+ "notify-lease-duration", 0);
3768+
3769+ resp = cupsDoRequest(m_connection, req,
3770+ getResource(CupsResourceRoot).toUtf8());
3771+ if (!isReplyOk(resp, true)) {
3772+ return subscriptionId;
3773+ }
3774+
3775+ attr = ippFindAttribute(resp, "notify-subscription-id", IPP_TAG_INTEGER);
3776+
3777+ if (!attr) {
3778+ qWarning() << "ipp-create-printer-subscription response doesn't"
3779+ " contain subscription id.";
3780+ } else {
3781+ subscriptionId = ippGetInteger(attr, 0);
3782+ }
3783+
3784+ ippDelete (resp);
3785+
3786+ return subscriptionId;
3787+}
3788+
3789+void IppClient::cancelSubscription(const int &subscriptionId)
3790+{
3791+ ipp_t *req;
3792+ ipp_t *resp;
3793+
3794+ if (subscriptionId <= 0) {
3795+ return;
3796+ }
3797+
3798+ req = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
3799+ ippAddString(req, IPP_TAG_OPERATION, IPP_TAG_URI,
3800+ "printer-uri", NULL, "/");
3801+ ippAddInteger(req, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
3802+ "notify-subscription-id", subscriptionId);
3803+
3804+ resp = cupsDoRequest(m_connection, req,
3805+ getResource(CupsResourceRoot).toUtf8());
3806+ if (!isReplyOk(resp, true)) {
3807+ return;
3808+ }
3809+
3810+ ippDelete(resp);
3811+}
3812+
3813+QVariant IppClient::getAttributeValue(ipp_attribute_t *attr, int index) const
3814+{
3815+ QVariant var;
3816+
3817+ if (ippGetCount(attr) > 1 && index < 0) {
3818+ QList<QVariant> list;
3819+
3820+ for (int i=0; i < ippGetCount(attr); i++) {
3821+ list.append(getAttributeValue(attr, i));
3822+ }
3823+
3824+ var = QVariant::fromValue<QList<QVariant>>(list);
3825+ } else {
3826+ if (index == -1) {
3827+ index = 0;
3828+ }
3829+
3830+ switch (ippGetValueTag(attr)) {
3831+ case IPP_TAG_NAME:
3832+ case IPP_TAG_TEXT:
3833+ case IPP_TAG_KEYWORD:
3834+ case IPP_TAG_URI:
3835+ case IPP_TAG_CHARSET:
3836+ case IPP_TAG_MIMETYPE:
3837+ case IPP_TAG_LANGUAGE:
3838+ var = QVariant::fromValue<QString>(ippGetString(attr, index, NULL));
3839+ break;
3840+ case IPP_TAG_INTEGER:
3841+ case IPP_TAG_ENUM:
3842+ var = QVariant::fromValue<int>(ippGetInteger(attr, index));
3843+ break;
3844+ case IPP_TAG_BOOLEAN:
3845+ var = QVariant::fromValue<bool>(ippGetBoolean(attr, index));
3846+ break;
3847+ case IPP_TAG_RANGE: {
3848+ QString range;
3849+ int upper;
3850+ int lower = ippGetRange(attr, index, &upper);
3851+
3852+ // Build a string similar to "1-3" "5-" "8" "-4"
3853+ if (lower != INT_MIN) {
3854+ range += QString::number(lower);
3855+ }
3856+
3857+ if (lower != upper) {
3858+ range += QStringLiteral("-");
3859+
3860+ if (upper != INT_MAX) {
3861+ range += QString::number(upper);
3862+ }
3863+ }
3864+
3865+ var = QVariant(range);
3866+ break;
3867+ }
3868+ case IPP_TAG_NOVALUE:
3869+ var = QVariant();
3870+ break;
3871+ case IPP_TAG_DATE: {
3872+ time_t time = ippDateToTime(ippGetDate(attr, index));
3873+ QDateTime datetime;
3874+ datetime.setTimeZone(QTimeZone::systemTimeZone());
3875+ datetime.setTime_t(time);
3876+
3877+ var = QVariant::fromValue<QDateTime>(datetime);
3878+ break;
3879+ }
3880+ default:
3881+ qWarning() << "Unknown IPP value tab 0x" << ippGetValueTag(attr);
3882+ break;
3883+ }
3884+ }
3885+
3886+ return var;
3887+}
3888+
3889+bool IppClient::getDevices(cups_device_cb_t callback, void *context) const
3890+{
3891+ auto reply = cupsGetDevices(m_connection, CUPS_TIMEOUT_DEFAULT,
3892+ CUPS_INCLUDE_ALL, CUPS_EXCLUDE_NONE, callback,
3893+ context);
3894+ return reply == IPP_OK;
3895+}
3896
3897=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h'
3898--- modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h 1970-01-01 00:00:00 +0000
3899+++ modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h 2017-03-13 15:55:01 +0000
3900@@ -0,0 +1,130 @@
3901+/*
3902+ * Copyright (C) 2017 Canonical, Ltd.
3903+ *
3904+ * This program is free software; you can redistribute it and/or modify
3905+ * it under the terms of the GNU Lesser General Public License as published by
3906+ * the Free Software Foundation; version 3.
3907+ *
3908+ * This program is distributed in the hope that it will be useful,
3909+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3910+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3911+ * GNU Lesser General Public License for more details.
3912+ *
3913+ * You should have received a copy of the GNU Lesser General Public License
3914+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3915+ */
3916+
3917+#ifndef USC_PRINTERS_CUPS_IPPCLIENT_H
3918+#define USC_PRINTERS_CUPS_IPPCLIENT_H
3919+
3920+#include "structs.h"
3921+
3922+#include <cups/adminutil.h>
3923+#include <cups/cups.h>
3924+#include <cups/http.h>
3925+#include <cups/ipp.h>
3926+#include <cups/ppd.h>
3927+
3928+#include <QString>
3929+#include <QStringList>
3930+
3931+/* From https://bugzilla.novell.com/show_bug.cgi?id=447444#c5
3932+ * We need to define a maximum length for strings to avoid cups
3933+ * thinking there are multiple lines.
3934+ */
3935+#define CPH_STR_MAXLEN 512
3936+
3937+class IppClient
3938+{
3939+public:
3940+ explicit IppClient();
3941+ ~IppClient();
3942+
3943+ bool printerDelete(const QString &printerName);
3944+ bool printerAdd(const QString &printerName,
3945+ const QString &printerUri,
3946+ const QString &ppdFile,
3947+ const QString &info,
3948+ const QString &location);
3949+
3950+ bool printerAddWithPpdFile(const QString &printerName,
3951+ const QString &printerUri,
3952+ const QString &ppdFileName,
3953+ const QString &info,
3954+ const QString &location);
3955+ bool printerHoldJob(const QString &printerName, const int jobId);
3956+ bool printerReleaseJob(const QString &printerName, const int jobId);
3957+ bool printerSetDefault(const QString &printerName);
3958+ bool printerSetEnabled(const QString &printerName, const bool enabled);
3959+ bool printerSetAcceptJobs(const QString &printerName, const bool accept,
3960+ const QString &reason);
3961+ bool printerSetShared(const QString &printerName, const bool shared);
3962+ bool printerClassSetInfo(const QString &name, const QString &info);
3963+ bool printerClassSetOption(const QString &name, const QString &option,
3964+ const QStringList &values);
3965+ ppd_file_t* getPpdFile(const QString &name, const QString &instance) const;
3966+ cups_dest_t* getDest(const QString &name, const QString &instance) const;
3967+
3968+ // Fetch more attributes, as normally just a small subset is fetched.
3969+ QMap<QString, QVariant> printerGetAttributes(const QString &printerName,
3970+ const QStringList &attributes);
3971+ QMap<QString, QVariant> printerGetJobAttributes(const QString &printerName,
3972+ const int jobId);
3973+
3974+ QString getLastError() const;
3975+
3976+ // Note: This response needs to be free by the caller.
3977+ ipp_t* createPrinterDriversRequest(
3978+ const QString &deviceId, const QString &language,
3979+ const QString &makeModel, const QString &product,
3980+ const QStringList &includeSchemes, const QStringList &excludeSchemes
3981+ );
3982+ int createSubscription();
3983+ void cancelSubscription(const int &subscriptionId);
3984+ bool getDevices(cups_device_cb_t callback, void *context) const;
3985+
3986+private:
3987+ enum CupsResource
3988+ {
3989+ CupsResourceRoot = 0,
3990+ CupsResourceAdmin,
3991+ CupsResourceJobs,
3992+ };
3993+
3994+ bool sendNewPrinterClassRequest(const QString &printerName,
3995+ ipp_tag_t group,
3996+ ipp_tag_t type,
3997+ const QString &name,
3998+ const QString &value);
3999+ static void addPrinterUri(ipp_t *request, const QString &name);
4000+ static void addRequestingUsername(ipp_t *request, const QString &username);
4001+ static const QString getResource(const CupsResource &resource);
4002+ static bool isPrinterNameValid(const QString &name);
4003+ static void addClassUri(ipp_t *request, const QString &name);
4004+ static bool isStringValid(const QString &string,
4005+ const bool checkNull = false,
4006+ const int maxLength = 512);
4007+ static bool isStringPrintable(const QString &string, const bool checkNull,
4008+ const int maxLength);
4009+ QString preparePpdForOptions(const QString &ppdfile,
4010+ cups_option_t *options,
4011+ int numOptions);
4012+ bool printerIsClass(const QString &name);
4013+ void setInternalStatus(const QString &status);
4014+ bool postRequest(ipp_t *request, const QString &file,
4015+ const CupsResource &resource);
4016+ bool sendRequest(ipp_t *request, const CupsResource &resource);
4017+ bool sendNewSimpleRequest(ipp_op_t op, const QString &printerName,
4018+ const CupsResource &resource);
4019+ bool handleReply(ipp_t *reply);
4020+ bool isReplyOk(ipp_t *reply, bool deleteIfReplyNotOk);
4021+ void setErrorFromReply(ipp_t *reply);
4022+ QVariant getAttributeValue(ipp_attribute_t *attr, int index=-1) const;
4023+
4024+ http_t *m_connection;
4025+ ipp_status_t m_lastStatus = IPP_OK;
4026+ mutable QString m_internalStatus = QString::null;
4027+};
4028+
4029+
4030+#endif // USC_PRINTERS_CUPS_IPPCLIENT_H
4031
4032=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.cpp'
4033--- modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.cpp 1970-01-01 00:00:00 +0000
4034+++ modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.cpp 2017-03-13 15:55:01 +0000
4035@@ -0,0 +1,127 @@
4036+/*
4037+ * Copyright (C) 2017 Canonical, Ltd.
4038+ *
4039+ * This program is free software; you can redistribute it and/or modify
4040+ * it under the terms of the GNU Lesser General Public License as published by
4041+ * the Free Software Foundation; version 3.
4042+ *
4043+ * This program is distributed in the hope that it will be useful,
4044+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4045+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4046+ * GNU Lesser General Public License for more details.
4047+ *
4048+ * You should have received a copy of the GNU Lesser General Public License
4049+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4050+ */
4051+
4052+#include "printerdriverloader.h"
4053+
4054+PrinterDriverLoader::PrinterDriverLoader(
4055+ const QString &deviceId, const QString &language,
4056+ const QString &makeModel, const QString &product,
4057+ const QStringList &includeSchemes, const QStringList &excludeSchemes)
4058+ : m_deviceId(deviceId)
4059+ , m_language(language)
4060+ , m_makeModel(makeModel)
4061+ , m_product(product)
4062+ , m_includeSchemes(includeSchemes)
4063+ , m_excludeSchemes(excludeSchemes)
4064+{
4065+}
4066+
4067+PrinterDriverLoader::~PrinterDriverLoader()
4068+{
4069+}
4070+
4071+void PrinterDriverLoader::process()
4072+{
4073+ m_running = true;
4074+
4075+ ipp_t* response = client.createPrinterDriversRequest(
4076+ m_deviceId, m_language, m_makeModel, m_product, m_includeSchemes,
4077+ m_excludeSchemes
4078+ );
4079+
4080+ // Note: if the response somehow fails, we return.
4081+ if (!response || ippGetStatusCode(response) > IPP_OK_CONFLICT) {
4082+ QString err(cupsLastErrorString());
4083+ qWarning() << Q_FUNC_INFO << "Cups HTTP error:" << err;
4084+
4085+ if (response)
4086+ ippDelete(response);
4087+
4088+ Q_EMIT error(err);
4089+ Q_EMIT finished();
4090+ return;
4091+ }
4092+
4093+ ipp_attribute_t *attr;
4094+ QByteArray ppdDeviceId;
4095+ QByteArray ppdLanguage;
4096+ QByteArray ppdMakeModel;
4097+ QByteArray ppdName;
4098+
4099+ QList<PrinterDriver> drivers;
4100+
4101+ for (attr = ippFirstAttribute(response); attr != NULL && m_running; attr = ippNextAttribute(response)) {
4102+
4103+ while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
4104+ attr = ippNextAttribute(response);
4105+
4106+ if (attr == NULL)
4107+ break;
4108+
4109+ // Pull the needed attributes from this PPD...
4110+ ppdDeviceId = "NONE";
4111+ ppdLanguage.clear();
4112+ ppdMakeModel.clear();
4113+ ppdName.clear();
4114+
4115+ while (attr != NULL && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
4116+ if (!strcmp(ippGetName(attr), "ppd-device-id") &&
4117+ ippGetValueTag(attr) == IPP_TAG_TEXT) {
4118+ ppdDeviceId = ippGetString(attr, 0, NULL);
4119+ } else if (!strcmp(ippGetName(attr), "ppd-natural-language") &&
4120+ ippGetValueTag(attr) == IPP_TAG_LANGUAGE) {
4121+ ppdLanguage = ippGetString(attr, 0, NULL);
4122+
4123+ } else if (!strcmp(ippGetName(attr), "ppd-make-and-model") &&
4124+ ippGetValueTag(attr) == IPP_TAG_TEXT) {
4125+ ppdMakeModel = ippGetString(attr, 0, NULL);
4126+ } else if (!strcmp(ippGetName(attr), "ppd-name") &&
4127+ ippGetValueTag(attr) == IPP_TAG_NAME) {
4128+
4129+ ppdName = ippGetString(attr, 0, NULL);
4130+ }
4131+
4132+ attr = ippNextAttribute(response);
4133+ }
4134+
4135+ // See if we have everything needed...
4136+ if (ppdLanguage.isEmpty() || ppdMakeModel.isEmpty() ||
4137+ ppdName.isEmpty()) {
4138+ if (attr == NULL)
4139+ break;
4140+ else
4141+ continue;
4142+ }
4143+
4144+ PrinterDriver m;
4145+ m.name = ppdName;
4146+ m.deviceId = ppdDeviceId;
4147+ m.makeModel = ppdMakeModel;
4148+ m.language = ppdLanguage;
4149+
4150+ drivers.append(m);
4151+ }
4152+
4153+ ippDelete(response);
4154+
4155+ Q_EMIT loaded(drivers);
4156+ Q_EMIT finished();
4157+}
4158+
4159+void PrinterDriverLoader::cancel()
4160+{
4161+ m_running = false;
4162+}
4163
4164=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.h'
4165--- modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.h 1970-01-01 00:00:00 +0000
4166+++ modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.h 2017-03-13 15:55:01 +0000
4167@@ -0,0 +1,61 @@
4168+/*
4169+ * Copyright (C) 2017 Canonical, Ltd.
4170+ *
4171+ * This program is free software; you can redistribute it and/or modify
4172+ * it under the terms of the GNU Lesser General Public License as published by
4173+ * the Free Software Foundation; version 3.
4174+ *
4175+ * This program is distributed in the hope that it will be useful,
4176+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4177+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4178+ * GNU Lesser General Public License for more details.
4179+ *
4180+ * You should have received a copy of the GNU Lesser General Public License
4181+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4182+ */
4183+
4184+#ifndef USC_PRINTERS_CUPS_DRIVERLOADER_H
4185+#define USC_PRINTERS_CUPS_DRIVERLOADER_H
4186+
4187+#include "ippclient.h"
4188+#include "structs.h"
4189+
4190+#include <QObject>
4191+#include <QString>
4192+#include <QStringList>
4193+
4194+class PrinterDriverLoader : public QObject
4195+{
4196+ Q_OBJECT
4197+public:
4198+ PrinterDriverLoader(
4199+ const QString &deviceId = "",
4200+ const QString &language = "",
4201+ const QString &makeModel = "",
4202+ const QString &product = "",
4203+ const QStringList &includeSchemes = QStringList(),
4204+ const QStringList &excludeSchemes = QStringList());
4205+ ~PrinterDriverLoader();
4206+
4207+public Q_SLOTS:
4208+ void process();
4209+ void cancel();
4210+
4211+Q_SIGNALS:
4212+ void finished();
4213+ void loaded(const QList<PrinterDriver> &drivers);
4214+ void error(const QString &error);
4215+
4216+private:
4217+ QString m_deviceId = QString::null;
4218+ QString m_language = QString::null;
4219+ QString m_makeModel = QString::null;
4220+ QString m_product = QString::null;
4221+ QStringList m_includeSchemes;
4222+ QStringList m_excludeSchemes;
4223+
4224+ bool m_running = false;
4225+ IppClient client;
4226+};
4227+
4228+#endif // USC_PRINTERS_CUPS_DRIVERLOADER_H
4229
4230=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp'
4231--- modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp 1970-01-01 00:00:00 +0000
4232+++ modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp 2017-03-13 15:55:01 +0000
4233@@ -0,0 +1,53 @@
4234+/*
4235+ * Copyright (C) 2017 Canonical, Ltd.
4236+ *
4237+ * This program is free software; you can redistribute it and/or modify
4238+ * it under the terms of the GNU Lesser General Public License as published by
4239+ * the Free Software Foundation; version 3.
4240+ *
4241+ * This program is distributed in the hope that it will be useful,
4242+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4243+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4244+ * GNU Lesser General Public License for more details.
4245+ *
4246+ * You should have received a copy of the GNU Lesser General Public License
4247+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4248+ */
4249+
4250+#include "backend/backend_pdf.h"
4251+#include "backend/backend_cups.h"
4252+#include "printerloader.h"
4253+
4254+#include <QPrinterInfo>
4255+
4256+class PrinterCupsBackend;
4257+PrinterLoader::PrinterLoader(const QString &printerName,
4258+ IppClient *client,
4259+ OrgCupsCupsdNotifierInterface* notifier,
4260+ QObject *parent)
4261+ : QObject(parent)
4262+ , m_printerName(printerName)
4263+ , m_client(client)
4264+ , m_notifier(notifier)
4265+{
4266+}
4267+
4268+PrinterLoader::~PrinterLoader()
4269+{
4270+}
4271+
4272+void PrinterLoader::load()
4273+{
4274+ QPrinterInfo info = QPrinterInfo::printerInfo(m_printerName);
4275+ auto backend = new PrinterCupsBackend(m_client, info, m_notifier);
4276+
4277+ // Dest or PPD was null, but we know it's name so we will use it.
4278+ if (info.printerName().isEmpty()) {
4279+ backend->setPrinterNameInternal(m_printerName);
4280+ }
4281+
4282+ auto p = QSharedPointer<Printer>(new Printer(backend));
4283+
4284+ Q_EMIT loaded(p);
4285+ Q_EMIT finished();
4286+}
4287
4288=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/printerloader.h'
4289--- modules/Ubuntu/Components/Extras/Printers/cups/printerloader.h 1970-01-01 00:00:00 +0000
4290+++ modules/Ubuntu/Components/Extras/Printers/cups/printerloader.h 2017-03-13 15:55:01 +0000
4291@@ -0,0 +1,49 @@
4292+/*
4293+ * Copyright (C) 2017 Canonical, Ltd.
4294+ *
4295+ * This program is free software; you can redistribute it and/or modify
4296+ * it under the terms of the GNU Lesser General Public License as published by
4297+ * the Free Software Foundation; version 3.
4298+ *
4299+ * This program is distributed in the hope that it will be useful,
4300+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4301+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4302+ * GNU Lesser General Public License for more details.
4303+ *
4304+ * You should have received a copy of the GNU Lesser General Public License
4305+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4306+ */
4307+
4308+#ifndef USC_PRINTERS_CUPS_PRINTERLOADER_H
4309+#define USC_PRINTERS_CUPS_PRINTERLOADER_H
4310+
4311+#include "cups/ippclient.h"
4312+#include "cupsdnotifier.h" // Note: this file was generated.
4313+#include "printer/printer.h"
4314+
4315+#include <QList>
4316+#include <QObject>
4317+#include <QSharedPointer>
4318+
4319+class PrinterLoader : public QObject
4320+{
4321+ Q_OBJECT
4322+ const QString m_printerName;
4323+ IppClient *m_client;
4324+ OrgCupsCupsdNotifierInterface *m_notifier;
4325+public:
4326+ explicit PrinterLoader(const QString &printerName,
4327+ IppClient *client,
4328+ OrgCupsCupsdNotifierInterface* notifier,
4329+ QObject *parent = Q_NULLPTR);
4330+ ~PrinterLoader();
4331+
4332+public Q_SLOTS:
4333+ void load();
4334+
4335+Q_SIGNALS:
4336+ void finished();
4337+ void loaded(QSharedPointer<Printer> printer);
4338+};
4339+
4340+#endif // USC_PRINTERS_CUPS_PRINTERLOADER_H
4341
4342=== added file 'modules/Ubuntu/Components/Extras/Printers/enums.h'
4343--- modules/Ubuntu/Components/Extras/Printers/enums.h 1970-01-01 00:00:00 +0000
4344+++ modules/Ubuntu/Components/Extras/Printers/enums.h 2017-03-13 15:55:01 +0000
4345@@ -0,0 +1,129 @@
4346+/*
4347+ * Copyright (C) 2017 Canonical, Ltd.
4348+ *
4349+ * This program is free software; you can redistribute it and/or modify
4350+ * it under the terms of the GNU Lesser General Public License as published by
4351+ * the Free Software Foundation; version 3.
4352+ *
4353+ * This program is distributed in the hope that it will be useful,
4354+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4355+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4356+ * GNU Lesser General Public License for more details.
4357+ *
4358+ * You should have received a copy of the GNU Lesser General Public License
4359+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4360+ */
4361+
4362+#ifndef USC_PRINTERS_ENUMS_H
4363+#define USC_PRINTERS_ENUMS_H
4364+
4365+#include "printers_global.h"
4366+
4367+#include <QtCore/QObject>
4368+
4369+class PRINTERS_DECL_EXPORT PrinterEnum
4370+{
4371+ Q_GADGET
4372+
4373+public:
4374+ enum class AccessControl
4375+ {
4376+ AccessAllow = 0,
4377+ AccessDeny,
4378+ };
4379+ Q_ENUM(AccessControl)
4380+
4381+ enum class ColorModelType
4382+ {
4383+ GrayType = 0,
4384+ ColorType,
4385+ UnknownType,
4386+ };
4387+ Q_ENUM(ColorModelType)
4388+
4389+ enum class DuplexMode
4390+ {
4391+ DuplexNone = 0,
4392+ DuplexLongSide,
4393+ DuplexShortSide,
4394+ };
4395+ Q_ENUM(DuplexMode)
4396+
4397+ enum class ErrorPolicy
4398+ {
4399+ RetryOnError = 0,
4400+ AbortOnError,
4401+ StopPrinterOnError,
4402+ RetryCurrentOnError,
4403+ };
4404+ Q_ENUM(ErrorPolicy)
4405+
4406+ // Match enums from ipp_jstate_t
4407+ enum class JobState
4408+ {
4409+ Pending = 3,
4410+ Held,
4411+ Processing,
4412+ Stopped,
4413+ Canceled,
4414+ Aborted,
4415+ Complete,
4416+ };
4417+ Q_ENUM(JobState)
4418+
4419+ enum class OperationPolicy
4420+ {
4421+ DefaultOperation = 0,
4422+ AuthenticatedOperation,
4423+ };
4424+ Q_ENUM(OperationPolicy)
4425+
4426+ enum class PrintRange
4427+ {
4428+ AllPages = 0,
4429+ PageRange,
4430+ };
4431+ Q_ENUM(PrintRange)
4432+
4433+ enum class State
4434+ {
4435+ IdleState = 0,
4436+ ActiveState,
4437+ AbortedState,
4438+ ErrorState,
4439+ };
4440+ Q_ENUM(State)
4441+
4442+ enum class PrinterType
4443+ {
4444+ ProxyType = 0, // Represents a printer not yet loaded.
4445+ CupsType,
4446+ PdfType,
4447+ };
4448+ Q_ENUM(PrinterType)
4449+
4450+ enum class DeviceType
4451+ {
4452+ UnknownType = 0,
4453+ LPDType,
4454+ IppSType,
4455+ Ipp14Type,
4456+ HttpType,
4457+ BehType,
4458+ SocketType,
4459+ HttpsType,
4460+ IppType,
4461+ HPType,
4462+ USBType,
4463+ HPFaxType,
4464+ DNSSDType,
4465+ };
4466+ Q_ENUM(DeviceType)
4467+};
4468+
4469+inline uint qHash(const PrinterEnum::JobState &state, uint seed)
4470+{
4471+ return qHash((int) state, seed);
4472+}
4473+
4474+#endif // USC_PRINTERS_ENUMS_H
4475
4476=== added file 'modules/Ubuntu/Components/Extras/Printers/i18n.cpp'
4477--- modules/Ubuntu/Components/Extras/Printers/i18n.cpp 1970-01-01 00:00:00 +0000
4478+++ modules/Ubuntu/Components/Extras/Printers/i18n.cpp 2017-03-13 15:55:01 +0000
4479@@ -0,0 +1,42 @@
4480+/*
4481+ * Copyright (C) 2014, 2017 Canonical, Ltd.
4482+ *
4483+ * This program is free software; you can redistribute it and/or modify
4484+ * it under the terms of the GNU Lesser General Public License as published by
4485+ * the Free Software Foundation; version 3.
4486+ *
4487+ * This program is distributed in the hope that it will be useful,
4488+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4489+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4490+ * GNU Lesser General Public License for more details.
4491+ *
4492+ * You should have received a copy of the GNU Lesser General Public License
4493+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4494+ *
4495+ * Authored by: Ken VanDine <ken.vandine@canonical.com>
4496+ * Andrew Hayzen <andrew.hayzen@canonical.com>
4497+ */
4498+
4499+#define NO_TR_OVERRIDE
4500+#include "i18n.h"
4501+
4502+#include <libintl.h>
4503+
4504+const char *thisDomain = "";
4505+
4506+void initTr(const char *domain, const char *localeDir)
4507+{
4508+ // Don't bind the domain or set textdomain as it changes the Apps domain
4509+ // as well. Instead store the domain and use it in the lookups
4510+ Q_UNUSED(localeDir);
4511+
4512+ thisDomain = domain;
4513+}
4514+
4515+QString __(const char *text, const char *domain)
4516+{
4517+ Q_UNUSED(domain);
4518+
4519+ // Use the stored domain
4520+ return QString::fromUtf8(dgettext(thisDomain, text));
4521+}
4522
4523=== added file 'modules/Ubuntu/Components/Extras/Printers/i18n.h'
4524--- modules/Ubuntu/Components/Extras/Printers/i18n.h 1970-01-01 00:00:00 +0000
4525+++ modules/Ubuntu/Components/Extras/Printers/i18n.h 2017-03-13 15:55:01 +0000
4526@@ -0,0 +1,29 @@
4527+/*
4528+ * Copyright (C) 2014, 2017 Canonical, Ltd.
4529+ *
4530+ * This program is free software; you can redistribute it and/or modify
4531+ * it under the terms of the GNU Lesser General Public License as published by
4532+ * the Free Software Foundation; version 3.
4533+ *
4534+ * This program is distributed in the hope that it will be useful,
4535+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4536+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4537+ * GNU Lesser General Public License for more details.
4538+ *
4539+ * You should have received a copy of the GNU Lesser General Public License
4540+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4541+ *
4542+ * Authored by: Ken VanDine <ken.vandine@canonical.com>
4543+ * Andrew Hayzen <andrew.hayzen@canonical.com>
4544+ */
4545+
4546+#ifndef I18N_H
4547+#define I18N_H
4548+
4549+#include <QtCore/QString>
4550+
4551+void initTr(const char *domain, const char *localeDir);
4552+QString __(const char *text, const char *domain = 0);
4553+
4554+#endif // I18N_H
4555+
4556
4557=== added directory 'modules/Ubuntu/Components/Extras/Printers/models'
4558=== added file 'modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp'
4559--- modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp 1970-01-01 00:00:00 +0000
4560+++ modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp 2017-03-13 15:55:01 +0000
4561@@ -0,0 +1,149 @@
4562+/*
4563+ * Copyright (C) 2017 Canonical, Ltd.
4564+ *
4565+ * This program is free software; you can redistribute it and/or modify
4566+ * it under the terms of the GNU Lesser General Public License as published by
4567+ * the Free Software Foundation; version 3.
4568+ *
4569+ * This program is distributed in the hope that it will be useful,
4570+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4571+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4572+ * GNU Lesser General Public License for more details.
4573+ *
4574+ * You should have received a copy of the GNU Lesser General Public License
4575+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4576+ */
4577+
4578+#include "backend/backend_cups.h"
4579+#include "models/devicemodel.h"
4580+
4581+#include <QDebug>
4582+
4583+DeviceModel::DeviceModel(PrinterBackend *backend, QObject *parent)
4584+ : QAbstractListModel(parent)
4585+ , m_backend(backend)
4586+ , m_isSearching(false)
4587+{
4588+ connect(m_backend, SIGNAL(deviceFound(const Device&)),
4589+ this, SLOT(deviceLoaded(const Device&)));
4590+ connect(m_backend, SIGNAL(deviceSearchFinished()),
4591+ this, SLOT(deviceSearchFinished()));
4592+}
4593+
4594+DeviceModel::~DeviceModel()
4595+{
4596+}
4597+
4598+int DeviceModel::rowCount(const QModelIndex &parent) const
4599+{
4600+ Q_UNUSED(parent);
4601+ return m_devices.size();
4602+}
4603+
4604+int DeviceModel::count() const
4605+{
4606+ return rowCount();
4607+}
4608+
4609+QVariant DeviceModel::data(const QModelIndex &index, int role) const
4610+{
4611+ QVariant ret;
4612+
4613+ if ((0 <= index.row()) && (index.row() < m_devices.size())) {
4614+
4615+ auto device = m_devices[index.row()];
4616+
4617+ switch (role) {
4618+ case Qt::DisplayRole:
4619+ ret = device.toString();
4620+ break;
4621+ case IdRole:
4622+ ret = device.id;
4623+ break;
4624+ case InfoRole:
4625+ ret = device.info;
4626+ break;
4627+ case UriRole:
4628+ ret = device.uri;
4629+ break;
4630+ case LocationRole:
4631+ ret = device.location;
4632+ break;
4633+ case TypeRole:
4634+ ret = QVariant::fromValue(device.type());
4635+ break;
4636+ case MakeModelRole:
4637+ ret = device.makeModel;
4638+ break;
4639+ }
4640+ }
4641+
4642+ return ret;
4643+}
4644+
4645+QHash<int, QByteArray> DeviceModel::roleNames() const
4646+{
4647+ static QHash<int,QByteArray> names;
4648+
4649+ if (Q_UNLIKELY(names.empty())) {
4650+ names[Qt::DisplayRole] = "displayName";
4651+ names[IdRole] = "id";
4652+ names[InfoRole] = "info";
4653+ names[UriRole] = "uri";
4654+ names[LocationRole] = "location";
4655+ names[TypeRole] = "type";
4656+ names[MakeModelRole] = "makeModel";
4657+ }
4658+
4659+ return names;
4660+}
4661+
4662+void DeviceModel::deviceLoaded(const Device &device)
4663+{
4664+ if (!deviceWanted(device)) {
4665+ return;
4666+ }
4667+
4668+ if (!m_devices.contains(device)) {
4669+ int i = m_devices.size();
4670+ beginInsertRows(QModelIndex(), i, i);
4671+ m_devices.append(device);
4672+ endInsertRows();
4673+
4674+ Q_EMIT countChanged();
4675+ }
4676+}
4677+
4678+bool DeviceModel::deviceWanted(const Device &device)
4679+{
4680+ auto parts = device.uri.split(":", QString::SkipEmptyParts);
4681+ return parts.size() > 1;
4682+}
4683+
4684+void DeviceModel::clear()
4685+{
4686+ beginResetModel();
4687+ m_devices.clear();
4688+ endResetModel();
4689+}
4690+
4691+void DeviceModel::load()
4692+{
4693+ if (m_isSearching) {
4694+ qWarning() << Q_FUNC_INFO
4695+ << "Ignoring load request as search is ongoing.";
4696+ } else {
4697+ clear();
4698+ if (m_backend->type() == PrinterEnum::PrinterType::CupsType) {
4699+ ((PrinterCupsBackend*) m_backend)->searchForDevices();
4700+ m_isSearching = true;
4701+ Q_EMIT searchingChanged();
4702+ }
4703+ }
4704+}
4705+
4706+void DeviceModel::deviceSearchFinished()
4707+{
4708+ m_isSearching = false;
4709+ Q_EMIT searchingChanged();
4710+}
4711
4712=== added file 'modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h'
4713--- modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h 1970-01-01 00:00:00 +0000
4714+++ modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h 2017-03-13 15:55:01 +0000
4715@@ -0,0 +1,78 @@
4716+/*
4717+ * Copyright (C) 2017 Canonical, Ltd.
4718+ *
4719+ * This program is free software; you can redistribute it and/or modify
4720+ * it under the terms of the GNU Lesser General Public License as published by
4721+ * the Free Software Foundation; version 3.
4722+ *
4723+ * This program is distributed in the hope that it will be useful,
4724+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4725+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4726+ * GNU Lesser General Public License for more details.
4727+ *
4728+ * You should have received a copy of the GNU Lesser General Public License
4729+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4730+ */
4731+
4732+#ifndef USC_PRINTER_DEVICEMODEL_H
4733+#define USC_PRINTER_DEVICEMODEL_H
4734+
4735+#include "printers_global.h"
4736+
4737+#include "structs.h"
4738+
4739+#include <QAbstractListModel>
4740+#include <QList>
4741+#include <QModelIndex>
4742+#include <QObject>
4743+#include <QVariant>
4744+
4745+class PRINTERS_DECL_EXPORT DeviceModel : public QAbstractListModel
4746+{
4747+ Q_OBJECT
4748+ Q_PROPERTY(int count READ count NOTIFY countChanged)
4749+ Q_PROPERTY(bool searching MEMBER m_isSearching NOTIFY searchingChanged)
4750+public:
4751+ explicit DeviceModel(PrinterBackend *backend, QObject *parent = Q_NULLPTR);
4752+ ~DeviceModel();
4753+
4754+ enum Roles
4755+ {
4756+ // Qt::DisplayRole holds device name
4757+ ClassRole = Qt::UserRole,
4758+ IdRole,
4759+ InfoRole,
4760+ UriRole,
4761+ LocationRole,
4762+ TypeRole,
4763+ MakeModelRole,
4764+ LastRole = MakeModelRole,
4765+ };
4766+
4767+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
4768+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
4769+ virtual QHash<int, QByteArray> roleNames() const override;
4770+
4771+ int count() const;
4772+ void load();
4773+ void clear();
4774+
4775+private Q_SLOTS:
4776+ void deviceLoaded(const Device &device);
4777+ void deviceSearchFinished();
4778+
4779+Q_SIGNALS:
4780+ void countChanged();
4781+ void searchingChanged();
4782+
4783+private:
4784+ /* Checks if we want it in the list or not. Wanted is intentionally
4785+ ambiguous. */
4786+ bool deviceWanted(const Device &device);
4787+
4788+ PrinterBackend *m_backend;
4789+ QList<Device> m_devices;
4790+ bool m_isSearching;
4791+};
4792+
4793+#endif // USC_PRINTER_DEVICEMODEL_H
4794
4795=== added file 'modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp'
4796--- modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp 1970-01-01 00:00:00 +0000
4797+++ modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp 2017-03-13 15:55:01 +0000
4798@@ -0,0 +1,175 @@
4799+/*
4800+ * Copyright (C) 2017 Canonical, Ltd.
4801+ *
4802+ * This program is free software; you can redistribute it and/or modify
4803+ * it under the terms of the GNU Lesser General Public License as published by
4804+ * the Free Software Foundation; version 3.
4805+ *
4806+ * This program is distributed in the hope that it will be useful,
4807+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4808+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4809+ * GNU Lesser General Public License for more details.
4810+ *
4811+ * You should have received a copy of the GNU Lesser General Public License
4812+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4813+ */
4814+
4815+#include "backend/backend_cups.h"
4816+#include "models/drivermodel.h"
4817+
4818+#include <QDebug>
4819+#include <QtConcurrent>
4820+
4821+DriverModel::DriverModel(PrinterBackend *backend, QObject *parent)
4822+ : QAbstractListModel(parent)
4823+ , m_backend(backend)
4824+{
4825+ connect(m_backend, SIGNAL(printerDriversLoaded(const QList<PrinterDriver>&)),
4826+ this, SLOT(printerDriversLoaded(const QList<PrinterDriver>&)));
4827+
4828+ QObject::connect(&m_watcher,
4829+ &QFutureWatcher<PrinterDriver>::finished,
4830+ this,
4831+ &DriverModel::filterFinished);
4832+
4833+}
4834+
4835+DriverModel::~DriverModel()
4836+{
4837+ cancel();
4838+}
4839+
4840+int DriverModel::rowCount(const QModelIndex &parent) const
4841+{
4842+ Q_UNUSED(parent);
4843+ return m_drivers.size();
4844+}
4845+
4846+int DriverModel::count() const
4847+{
4848+ return rowCount();
4849+}
4850+
4851+QVariant DriverModel::data(const QModelIndex &index, int role) const
4852+{
4853+ QVariant ret;
4854+
4855+ if ((0 <= index.row()) && (index.row() < m_drivers.size())) {
4856+
4857+ auto driver = m_drivers[index.row()];
4858+
4859+ switch (role) {
4860+ case Qt::DisplayRole:
4861+ ret = driver.toString();
4862+ break;
4863+ case NameRole:
4864+ ret = driver.name;
4865+ break;
4866+ case DeviceIdRole:
4867+ ret = driver.deviceId;
4868+ break;
4869+ case LanguageRole:
4870+ ret = driver.language;
4871+ break;
4872+ case MakeModelRole:
4873+ ret = driver.makeModel;
4874+ break;
4875+ }
4876+ }
4877+
4878+ return ret;
4879+}
4880+
4881+QHash<int, QByteArray> DriverModel::roleNames() const
4882+{
4883+ static QHash<int,QByteArray> names;
4884+
4885+ if (Q_UNLIKELY(names.empty())) {
4886+ names[Qt::DisplayRole] = "displayName";
4887+ names[NameRole] = "name";
4888+ names[DeviceIdRole] = "deviceId";
4889+ names[LanguageRole] = "language";
4890+ names[MakeModelRole] = "makeModel";
4891+ }
4892+
4893+ return names;
4894+}
4895+
4896+void DriverModel::setFilter(const QString& pattern)
4897+{
4898+ QList<QByteArray> needles;
4899+ Q_FOREACH(const QString patternPart, pattern.toLower().split(" ")) {
4900+ needles.append(patternPart.toUtf8());
4901+ }
4902+ QList<PrinterDriver> list;
4903+
4904+ if (m_watcher.isRunning())
4905+ m_watcher.cancel();
4906+
4907+ if (pattern.isEmpty()) {
4908+ setModel(m_originalDrivers);
4909+ m_filter = pattern;
4910+ return;
4911+ }
4912+
4913+ if (!m_filter.isEmpty() && !m_drivers.isEmpty() &&
4914+ pattern.startsWith(m_filter))
4915+ list = m_drivers; // search in the smaller list
4916+ else
4917+ list = m_originalDrivers; //search in the whole list
4918+
4919+ m_filter = pattern;
4920+
4921+ QFuture<PrinterDriver> future(QtConcurrent::filtered(list,
4922+ [needles] (const PrinterDriver &driver) {
4923+ QByteArray haystack = driver.makeModel.toLower();
4924+ Q_FOREACH(const QByteArray needle, needles) {
4925+ if (!haystack.contains(needle)) {
4926+ return false;
4927+ }
4928+ }
4929+ return true;
4930+ }
4931+ )
4932+ );
4933+
4934+ Q_EMIT filterBegin();
4935+
4936+ m_watcher.setFuture(future);
4937+}
4938+
4939+QString DriverModel::filter() const
4940+{
4941+ return m_filter;
4942+}
4943+
4944+void DriverModel::filterFinished()
4945+{
4946+ setModel(m_watcher.future().results());
4947+}
4948+
4949+void DriverModel::load()
4950+{
4951+ m_backend->requestPrinterDrivers();
4952+}
4953+
4954+void DriverModel::cancel()
4955+{
4956+ if (m_watcher.isRunning())
4957+ m_watcher.cancel();
4958+}
4959+
4960+void DriverModel::printerDriversLoaded(const QList<PrinterDriver> &drivers)
4961+{
4962+ m_originalDrivers = drivers;
4963+ setModel(m_originalDrivers);
4964+}
4965+
4966+void DriverModel::setModel(const QList<PrinterDriver> &drivers)
4967+{
4968+ beginResetModel();
4969+ m_drivers = drivers;
4970+ endResetModel();
4971+
4972+ Q_EMIT filterComplete();
4973+}
4974
4975=== added file 'modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h'
4976--- modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h 1970-01-01 00:00:00 +0000
4977+++ modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h 2017-03-13 15:55:01 +0000
4978@@ -0,0 +1,82 @@
4979+/*
4980+ * Copyright (C) 2017 Canonical, Ltd.
4981+ *
4982+ * This program is free software; you can redistribute it and/or modify
4983+ * it under the terms of the GNU Lesser General Public License as published by
4984+ * the Free Software Foundation; version 3.
4985+ *
4986+ * This program is distributed in the hope that it will be useful,
4987+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4988+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4989+ * GNU Lesser General Public License for more details.
4990+ *
4991+ * You should have received a copy of the GNU Lesser General Public License
4992+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4993+ */
4994+
4995+#ifndef USC_PRINTER_DRIVERMODEL_H
4996+#define USC_PRINTER_DRIVERMODEL_H
4997+
4998+#include "printers_global.h"
4999+
5000+#include "structs.h"
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches