diff --git a/widget/nsCUPSShim.h b/widget/nsCUPSShim.h index ddbc278eb0e8..29816bc17eba 100644 --- a/widget/nsCUPSShim.h +++ b/widget/nsCUPSShim.h @@ -58,14 +58,19 @@ class nsCUPSShim { X(Optional::No, cupsGetDests) \ X(Optional::No, cupsGetNamedDest) \ X(Optional::No, cupsGetOption) \ + X(Optional::No, cupsServer) \ + X(Optional::Yes, httpAddrPort) \ X(Optional::No, httpClose) \ + X(Optional::No, httpGetHostname) \ + X(Optional::Yes, httpGetAddress) \ X(Optional::No, ippAddString) \ X(Optional::No, ippAddStrings) \ X(Optional::No, ippDelete) \ X(Optional::No, ippFindAttribute) \ X(Optional::No, ippGetCount) \ X(Optional::No, ippGetString) \ - X(Optional::No, ippNewRequest) + X(Optional::No, ippNewRequest) \ + X(Optional::No, ippPort) #ifdef CUPS_SHIM_RUNTIME_LINK // Define a single field which holds a function pointer. diff --git a/widget/nsPrinterCUPS.cpp b/widget/nsPrinterCUPS.cpp index aea739d73fee..413bb90923aa 100644 --- a/widget/nsPrinterCUPS.cpp +++ b/widget/nsPrinterCUPS.cpp @@ -149,8 +149,7 @@ bool nsPrinterCUPS::SupportsColor() const { bool nsPrinterCUPS::SupportsCollation() const { // We can't depend on cupsGetIntegerOption existing. - const char* const value = mShim.cupsGetOption( - "printer-type", mPrinter->num_options, mPrinter->options); + const char* const value = FindCUPSOption("printer-type"); if (!value) { return false; } @@ -350,6 +349,77 @@ nsPrinterCUPS::PrinterInfoLock nsPrinterCUPS::TryEnsurePrinterInfo( lock->mTriedInitWithConnection = true; } + MOZ_ASSERT(mPrinter); + + // 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. + + const char* const serverNameBytes = mShim.cupsServer(); + + 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(hostnameMemLength); + + // We don't expect httpGetHostname to return null when a connection is + // passed, but it's better not to make assumptions. + const char* const hostnameBytes = mShim.httpGetHostname( + aConnection, hostnameMem.get(), hostnameMemLength); + + if (MOZ_LIKELY(hostnameBytes)) { + const nsDependentCString hostname{hostnameBytes}; + + // Match the condional at + // https://github.com/apple/cups/blob/c9da6f63b263faef5d50592fe8cf8056e0a58aa2/cups/dest-options.c#L718 + const bool portsDiffer = + mShim.httpAddrPort(mShim.httpGetAddress(aConnection)) != + mShim.ippPort(); + const bool cupsDestDeviceFlag = + (hostname != serverName && serverName[0] != '/') || portsDiffer; + + // Match the conditional at + // https://github.com/apple/cups/blob/23c45db76a8520fd6c3b1d9164dbe312f1ab1481/cups/dest.c#L1144 + // but if device-uri is null do not call into CUPS. + if ((cupsDestDeviceFlag || !FindCUPSOption("printer-uri-supported")) && + !FindCUPSOption("device-uri")) { + return lock; + } + } + } + } + // 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. diff --git a/widget/nsPrinterCUPS.h b/widget/nsPrinterCUPS.h index a917a0d6bc3d..bb15b75b3fb9 100644 --- a/widget/nsPrinterCUPS.h +++ b/widget/nsPrinterCUPS.h @@ -48,6 +48,10 @@ class nsPrinterCUPS final : public nsPrinterBase { static void ForEachExtraMonochromeSetting( mozilla::FunctionRef); + inline const char* FindCUPSOption(const char* name) const { + return mShim.cupsGetOption(name, mPrinter->num_options, mPrinter->options); + } + private: struct CUPSPrinterInfo { cups_dinfo_t* mPrinterInfo = nullptr;