/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2; -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
static PaperInfo MakePaperInfo(const nsAString& aName, const cups_size_t& aMedia) { // XXX Do we actually have the guarantee that this is utf-8?
NS_ConvertUTF8toUTF16 paperId(aMedia.media); // internal paper name/ID return PaperInfo(
paperId, aName,
{aMedia.width * kPointsPerHundredthMillimeter,
aMedia.length * kPointsPerHundredthMillimeter},
Some(gfx::MarginDouble{aMedia.top * kPointsPerHundredthMillimeter,
aMedia.right * kPointsPerHundredthMillimeter,
aMedia.bottom * kPointsPerHundredthMillimeter,
aMedia.left * kPointsPerHundredthMillimeter}));
}
// Fetches the CUPS version for the print server controlling the printer. This // will only modify the output arguments if the fetch succeeds. staticvoid FetchCUPSVersionForPrinter(const nsCUPSShim& aShim, const cups_dest_t* const aDest,
uint64_t& aOutMajor, uint64_t& aOutMinor,
uint64_t& aOutPatch) { // Make an IPP request to the server for the printer. constchar* const uri = aShim.cupsGetOption( "printer-uri-supported", aDest->num_options, aDest->options); if (!uri) { return;
}
// Set the URI we want to use.
aShim.ippAddString(ippRequest, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
nullptr, uri);
// Set the attributes to request.
aShim.ippAddStrings(ippRequest, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", requestedAttributes.Length,
nullptr, &(requestedAttributes[0]));
// Use the default HTTP connection to query the CUPS server itself to get // the CUPS version. // Note that cupsDoRequest will delete the request whether it succeeds or // fails, so we should not use ippDelete on it. if (ipp_t* const ippResponse =
aShim.cupsDoRequest(CUPS_HTTP_DEFAULT, ippRequest, "/")) {
ipp_attribute_t* const versionAttrib =
aShim.ippFindAttribute(ippResponse, "cups-version", IPP_TAG_TEXT); if (versionAttrib && aShim.ippGetCount(versionAttrib) == 1) { constchar* versionString = aShim.ippGetString(versionAttrib, 0, nullptr);
MOZ_ASSERT(versionString); // On error, GkRustUtils::ParseSemVer will not modify its arguments.
GkRustUtils::ParseSemVer(
nsDependentCSubstring{MakeStringSpan(versionString)}, aOutMajor,
aOutMinor, aOutPatch);
}
aShim.ippDelete(ippResponse);
}
}
nsPrinterCUPS::~nsPrinterCUPS() {
PrinterInfoLock lock = mPrinterInfoMutex.Lock(); if (lock->mPrinterInfo) {
mShim.cupsFreeDestInfo(lock->mPrinterInfo);
} if (lock->mPrinter) {
mShim.cupsFreeDests(1, lock->mPrinter);
}
}
bool nsPrinterCUPS::SupportsColor() const { // CUPS 2.1 (particularly as used in Ubuntu 16) is known to have inaccurate // results for CUPS_PRINT_COLOR_MODE. // See https://bugzilla.mozilla.org/show_bug.cgi?id=1660658#c15 if (!IsCUPSVersionAtLeast(2, 2, 0)) { returntrue; // See comment for PrintSettingsInitializer.mPrintInColor
} return Supports(CUPS_PRINT_COLOR_MODE, CUPS_PRINT_COLOR_MODE_AUTO) ||
Supports(CUPS_PRINT_COLOR_MODE, CUPS_PRINT_COLOR_MODE_COLOR) ||
!Supports(CUPS_PRINT_COLOR_MODE, CUPS_PRINT_COLOR_MODE_MONOCHROME);
}
bool nsPrinterCUPS::SupportsCollation() const { // We can't depend on cupsGetIntegerOption existing.
PrinterInfoLock lock = mPrinterInfoMutex.Lock(); constchar* const value = FindCUPSOption(lock, "printer-type"); if (!value) { returnfalse;
} // If the value is non-numeric, then atoi will return 0, which will still // cause this function to return false. constint type = atoi(value); return type & CUPS_PRINTER_COLLATE;
}
bool hasDefaultMedia = false; // cupsGetDestMediaDefault appears to return more accurate defaults on macOS, // and the IPP attribute appears to return more accurate defaults on Linux. #ifdef XP_MACOSX
hasDefaultMedia = mShim.cupsGetDestMediaDefault(
CUPS_HTTP_DEFAULT, lock->mPrinter, lock->mPrinterInfo,
CUPS_MEDIA_FLAGS_DEFAULT, &media); #else
{
ipp_attribute_t* defaultMediaIPP =
mShim.cupsFindDestDefault
? mShim.cupsFindDestDefault(CUPS_HTTP_DEFAULT, lock->mPrinter,
lock->mPrinterInfo, "media")
: nullptr;
if (!hasDefaultMedia) { return PrintSettingsInitializer{
std::move(printerName),
PaperInfo(),
SupportsColor(),
};
}
// Check if this is a localized fallback paper size, in which case we can // avoid using the CUPS localization methods. const gfx::SizeDouble sizeDouble{
media.width * kPointsPerHundredthMillimeter,
media.length * kPointsPerHundredthMillimeter}; if (const PaperInfo* const paperInfo = FindCommonPaperSize(sizeDouble)) { return PrintSettingsInitializer{
std::move(printerName),
MakePaperInfo(paperInfo->mName, media),
SupportsColor(),
};
}
http_t* const connection = aConnection.GetConnection(lock->mPrinter); // XXX Do we actually have the guarantee that this is utf-8?
NS_ConvertUTF8toUTF16 localizedName{
connection ? LocalizeMediaName(*connection, media) : ""};
paperList.SetCapacity(paperCount); for (int i = 0; i < paperCount; ++i) {
cups_size_t media; constint getInfoSucceeded = mShim.cupsGetDestMediaByIndex(
connection, lock->mPrinter, lock->mPrinterInfo, i,
CUPS_MEDIA_FLAGS_DEFAULT, &media);
if (!getInfoSucceeded || !paperSet.EnsureInserted(media.media)) { continue;
} // Check if this is a PWG paper size, in which case we can avoid using the // CUPS localization methods. const gfx::SizeDouble sizeDouble{
media.width * kPointsPerHundredthMillimeter,
media.length * kPointsPerHundredthMillimeter}; if (const PaperInfo* const paperInfo = FindCommonPaperSize(sizeDouble)) {
paperList.AppendElement(MakePaperInfo(paperInfo->mName, media));
} else { constchar* const mediaName =
connection ? LocalizeMediaName(*connection, media) : media.media;
paperList.AppendElement(
MakePaperInfo(NS_ConvertUTF8toUTF16(mediaName), media));
}
}
// httpGetAddress was only added in CUPS 2.0, and some systems still use // CUPS 1.7. if (aConnection && MOZ_LIKELY(mShim.httpGetAddress && mShim.httpAddrPort)) { // This is a workaround for the CUPS Bug seen in bug 1691347. // This is to avoid a null string being passed to strstr in CUPS. The path // in CUPS that leads to this is as follows: // // In cupsCopyDestInfo, CUPS_DEST_FLAG_DEVICE is set when the connection is // not null (same as CUPS_HTTP_DEFAULT), the print server is not the same // as our hostname and is not path-based (starts with a '/'), or the IPP // port is different than the global server IPP port. // // https://github.com/apple/cups/blob/c9da6f63b263faef5d50592fe8cf8056e0a58aa2/cups/dest-options.c#L718-L722 // // In _cupsGetDestResource, CUPS fetches the IPP options "device-uri" and // "printer-uri-supported". Note that IPP options are returned as null when // missing. // // https://github.com/apple/cups/blob/23c45db76a8520fd6c3b1d9164dbe312f1ab1481/cups/dest.c#L1138-L1141 // // If the CUPS_DEST_FLAG_DEVICE is set or the "printer-uri-supported" // option is not set, CUPS checks for "._tcp" in the "device-uri" option // without doing a NULL-check first. // // https://github.com/apple/cups/blob/23c45db76a8520fd6c3b1d9164dbe312f1ab1481/cups/dest.c#L1144 // // If we find that those branches will be taken, don't actually fetch the // CUPS data and instead just return an empty printer info.
if (MOZ_LIKELY(serverNameBytes)) { const nsDependentCString serverName{serverNameBytes};
// We only need enough characters to determine equality with serverName. // + 2 because we need one byte for the null-character, and we also want // to get more characters of the host name than the server name if // possible. Otherwise, if the hostname starts with the same text as the // entire server name, it would compare equal when it's not. const size_t hostnameMemLength = serverName.Length() + 2; auto hostnameMem = MakeUnique<char[]>(hostnameMemLength);
// We don't expect httpGetHostname to return null when a connection is // passed, but it's better not to make assumptions. constchar* const hostnameBytes = mShim.httpGetHostname(
aConnection, hostnameMem.get(), hostnameMemLength);
if (MOZ_LIKELY(hostnameBytes)) { const nsDependentCString hostname{hostnameBytes};
// Attempt to match the condional at // https://github.com/apple/cups/blob/c9da6f63b263faef5d50592fe8cf8056e0a58aa2/cups/dest-options.c#L718 // // To find the result of the comparison CUPS performs of // `strcmp(http->hostname, cg->server)`, we use httpGetHostname to try // to get the value of `http->hostname`, but this isn't quite the same. // For local addresses, httpGetHostName will normalize the result to be // localhost", rather than the actual value of `http->hostname`. // // https://github.com/apple/cups/blob/2201569857f225c9874bfae19713ffb2f4bdfdeb/cups/http-addr.c#L794-L818 // // Because of this, if both serverName and hostname equal "localhost", // then the actual hostname might be a different local address that CUPS // normalized in httpGetHostName, and `http->hostname` won't be equal to // `cg->server` in CUPS. constbool namesMightNotMatch =
hostname != serverName || hostname == "localhost"; constbool portsDiffer =
mShim.httpAddrPort(mShim.httpGetAddress(aConnection)) !=
mShim.ippPort(); constbool cupsDestDeviceFlag =
(namesMightNotMatch && serverName[0] != '/') || portsDiffer;
// All CUPS calls that take the printer info do null-checks internally, so we // can fetch this info and only worry about the result of the later CUPS // functions.
aLock->mPrinterInfo = mShim.cupsCopyDestInfo(aConnection, aLock->mPrinter);
// Even if we failed to fetch printer info, it is still possible we can talk // to the print server and get its CUPS version.
FetchCUPSVersionForPrinter(mShim, aLock->mPrinter, aLock->mCUPSMajor,
aLock->mCUPSMinor, aLock->mCUPSPatch);
}