1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsPrinterListCUPS.h"
7 #include "mozilla/IntegerRange.h"
8 #include "mozilla/Maybe.h"
9 #include "mozilla/StaticPrefs_print.h"
10 #include "nsCUPSShim.h"
11 #include "nsPrinterCUPS.h"
15 // Use a local static to initialize the CUPS shim lazily, when it's needed.
16 // This is used in order to avoid a global constructor.
17 static nsCUPSShim
& CupsShim() {
18 static nsCUPSShim sCupsShim
;
22 using PrinterInfo
= nsPrinterListBase::PrinterInfo
;
25 * Retrieves a human-readable name for the printer from CUPS.
26 * https://www.cups.org/doc/cupspm.html#basic-destination-information
28 static void GetDisplayNameForPrinter(const cups_dest_t
& aDest
,
30 // macOS clients expect prettified printer names
31 // while GTK clients expect non-prettified names.
32 // If you change this, please change NamedPrinter accordingly.
34 const char* displayName
= CupsShim().cupsGetOption(
35 "printer-info", aDest
.num_options
, aDest
.options
);
37 CopyUTF8toUTF16(mozilla::MakeStringSpan(displayName
), aName
);
43 nsPrinterListCUPS::InitPrintSettingsFromPrinter(
44 const nsAString
& aPrinterName
, nsIPrintSettings
* aPrintSettings
) {
45 MOZ_ASSERT(aPrintSettings
);
47 // Set a default file name.
48 nsAutoString filename
;
49 nsresult rv
= aPrintSettings
->GetToFileName(filename
);
50 if (NS_FAILED(rv
) || filename
.IsEmpty()) {
51 const char* path
= PR_GetEnv("PWD");
53 path
= PR_GetEnv("HOME");
57 CopyUTF8toUTF16(mozilla::MakeStringSpan(path
), filename
);
58 filename
.AppendLiteral("/mozilla.pdf");
60 filename
.AssignLiteral("mozilla.pdf");
63 aPrintSettings
->SetToFileName(filename
);
66 aPrintSettings
->SetIsInitializedFromPrinter(true);
70 static int CupsDestCallback(void* user_data
, unsigned aFlags
,
72 MOZ_ASSERT(user_data
);
73 auto* printerInfoList
= static_cast<nsTArray
<PrinterInfo
>*>(user_data
);
75 cups_dest_t
* ownedDest
= nullptr;
76 mozilla::DebugOnly
<const int> numCopied
=
77 CupsShim().cupsCopyDest(aDest
, 0, &ownedDest
);
78 MOZ_ASSERT(numCopied
== 1);
81 GetDisplayNameForPrinter(*aDest
, name
);
83 printerInfoList
->AppendElement(PrinterInfo
{std::move(name
), ownedDest
});
85 return aFlags
== CUPS_DEST_FLAGS_MORE
? 1 : 0;
88 nsTArray
<PrinterInfo
> nsPrinterListCUPS::Printers() const {
89 if (!CupsShim().InitOkay()) {
93 auto FreeDestsAndClear
= [](nsTArray
<PrinterInfo
>& aArray
) {
94 for (auto& info
: aArray
) {
95 CupsShim().cupsFreeDests(1, static_cast<cups_dest_t
*>(info
.mCupsHandle
));
100 nsTArray
<PrinterInfo
> printerInfoList
;
101 // cupsGetDests2 returns list of found printers without duplicates, unlike
103 cups_dest_t
* printers
= nullptr;
104 const auto numPrinters
= CupsShim().cupsGetDests2(nullptr, &printers
);
105 if (numPrinters
> 0) {
106 for (auto i
: mozilla::IntegerRange(0, numPrinters
)) {
107 cups_dest_t
* ownedDest
= nullptr;
108 mozilla::DebugOnly
<const int> numCopied
=
109 CupsShim().cupsCopyDest(printers
+ i
, 0, &ownedDest
);
110 MOZ_ASSERT(numCopied
== 1);
113 GetDisplayNameForPrinter(*(printers
+ i
), name
);
114 printerInfoList
.AppendElement(PrinterInfo
{std::move(name
), ownedDest
});
116 CupsShim().cupsFreeDests(numPrinters
, printers
);
117 return printerInfoList
;
120 // An error occurred - retry with CUPS_PRINTER_DISCOVERED masked out (since
121 // it looks like there are a lot of error cases for that in cupsEnumDests):
122 if (CupsShim().cupsEnumDests(
123 CUPS_DEST_FLAGS_NONE
,
124 0 /* 0 timeout should be okay when masking CUPS_PRINTER_DISCOVERED */,
125 nullptr /* cancel* */, CUPS_PRINTER_LOCAL
,
126 CUPS_PRINTER_FAX
| CUPS_PRINTER_SCANNER
| CUPS_PRINTER_DISCOVERED
,
127 &CupsDestCallback
, &printerInfoList
)) {
128 return printerInfoList
;
131 // Another error occurred. Maybe printerInfoList could be partially
132 // populated, so perhaps we could return it without clearing it in the hope
133 // that there are some usable dests. However, presuambly CUPS doesn't
134 // guarantee that any dests that it added are complete and safe to use when
136 FreeDestsAndClear(printerInfoList
);
140 RefPtr
<nsIPrinter
> nsPrinterListCUPS::CreatePrinter(PrinterInfo aInfo
) const {
141 return mozilla::MakeRefPtr
<nsPrinterCUPS
>(
142 mCommonPaperInfo
, CupsShim(), std::move(aInfo
.mName
),
143 static_cast<cups_dest_t
*>(aInfo
.mCupsHandle
));
146 mozilla::Maybe
<PrinterInfo
> nsPrinterListCUPS::PrinterByName(
147 nsString aPrinterName
) const {
148 mozilla::Maybe
<PrinterInfo
> rv
;
149 if (!CupsShim().InitOkay()) {
153 // Will contain the printer, if found. This must be fully owned, and not a
154 // member of another array of printers.
155 cups_dest_t
* printer
= nullptr;
158 // On OS X the printer name given to this function is the readable/display
159 // name and not the CUPS name, so we iterate over all the printers for now.
160 // See bug 1659807 for one approach to improve perf here.
162 nsAutoCString printerName
;
163 CopyUTF16toUTF8(aPrinterName
, printerName
);
164 cups_dest_t
* printers
= nullptr;
165 const auto numPrinters
= CupsShim().cupsGetDests(&printers
);
166 for (auto i
: mozilla::IntegerRange(0, numPrinters
)) {
167 const char* const displayName
= CupsShim().cupsGetOption(
168 "printer-info", printers
[i
].num_options
, printers
[i
].options
);
169 if (printerName
== displayName
) {
170 // The second arg to CupsShim().cupsCopyDest is called num_dests, but
171 // it actually copies num_dests + 1 elements.
172 CupsShim().cupsCopyDest(printers
+ i
, 0, &printer
);
176 CupsShim().cupsFreeDests(numPrinters
, printers
);
179 // On GTK, we only ever show the CUPS name of printers, so we can use
180 // cupsGetNamedDest directly.
182 const auto printerName
= NS_ConvertUTF16toUTF8(aPrinterName
);
183 printer
= CupsShim().cupsGetNamedDest(CUPS_HTTP_DEFAULT
, printerName
.get(),
189 // Since the printer name had to be passed by-value, we can move the
191 rv
.emplace(PrinterInfo
{std::move(aPrinterName
), printer
});
196 mozilla::Maybe
<PrinterInfo
> nsPrinterListCUPS::PrinterBySystemName(
197 nsString aPrinterName
) const {
198 mozilla::Maybe
<PrinterInfo
> rv
;
199 if (!CupsShim().InitOkay()) {
203 const auto printerName
= NS_ConvertUTF16toUTF8(aPrinterName
);
204 if (cups_dest_t
* const printer
= CupsShim().cupsGetNamedDest(
205 CUPS_HTTP_DEFAULT
, printerName
.get(), nullptr)) {
206 rv
.emplace(PrinterInfo
{std::move(aPrinterName
), printer
});
211 nsresult
nsPrinterListCUPS::SystemDefaultPrinterName(nsAString
& aName
) const {
214 if (!CupsShim().InitOkay()) {
218 // Passing in nullptr for the name will return the default, if any.
220 CupsShim().cupsGetNamedDest(CUPS_HTTP_DEFAULT
, /* name */ nullptr,
221 /* instance */ nullptr);
226 GetDisplayNameForPrinter(*dest
, aName
);
227 if (aName
.IsEmpty()) {
228 CopyUTF8toUTF16(mozilla::MakeStringSpan(dest
->name
), aName
);
231 CupsShim().cupsFreeDests(1, dest
);