Skip to content

Commit

Permalink
Implement BrowseDomains, FilterLocation, and FilterType directives in
Browse files Browse the repository at this point in the history
client.conf (Issue #1180)
  • Loading branch information
michaelrsweet committed Feb 28, 2025
1 parent 147715c commit 2371ba8
Show file tree
Hide file tree
Showing 12 changed files with 818 additions and 43 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ libcups v3.0rc4 (YYYY-MM-DD)
- Added `cupsCopyCredentialsPublicKey` API.
- Added `cupsGetClock` API.
- Added `cupsJWTLoadCredentials` API.
- Added "client.conf" man page.
- Updated documentation (Issue #99)
- Updated the `ipptool` utility to support the `--bearer-token` and
`--client-name` options.
- Updated `cupsOAuthGetMetadata` to support Microsoft Azure/Entra OAuth
servers.
- Updated `ipptransform` to support generation of PCLm output in addition to PWG
Raster data.
- Updated `cupsEnumDests` and `cupsGetDests` to support printer browsing and
filtering options in client.conf (Issue #1180)
- Fixed handling of finishings/finishings-col and media/media-col in the
`ippeveprinter` tool (Issue #95)
- Fixed a duplicate printer reporting bug in `cupsGetDests`.
Expand Down
14 changes: 13 additions & 1 deletion cups/cups-private.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// Private definitions for CUPS.
//
// Copyright © 2021-2024 by OpenPrinting.
// Copyright © 2021-2025 by OpenPrinting.
// Copyright © 2007-2019 by Apple Inc.
// Copyright © 1997-2007 by Easy Software Products, all rights reserved.
//
Expand Down Expand Up @@ -30,6 +30,7 @@ extern "C" {
# ifdef _WIN32
typedef int mode_t; // Windows doesn't support mode_t type @private@
# endif // _WIN32
# include <regex.h>


//
Expand Down Expand Up @@ -144,6 +145,8 @@ typedef struct _cups_globals_s // CUPS global state data
char tempfile[1024]; // cupsTempFd/File buffer

// usersys.c
bool client_conf_loaded;
// Has client.conf been loaded?
_cups_digestoptions_t digestoptions; // DigestOptions setting
_cups_uatokens_t uatokens; // UserAgentTokens setting
http_encryption_t encryption; // Encryption setting
Expand All @@ -163,6 +166,15 @@ typedef struct _cups_globals_s // CUPS global state data
expired_certs, // Allow expired certs
validate_certs; // Validate certificates

cups_array_t *browse_domains;// BrowseDomains list
cups_array_t *filter_location_array;
// FilterLocation list
regex_t *filter_location_regex;
// FilterLocation regular expression
cups_ptype_t filter_type; // FilterType values from client.conf
cups_ptype_t filter_type_mask;
// FilterType mask

// util.c
char def_printer[256];
// Default printer
Expand Down
143 changes: 117 additions & 26 deletions cups/dest.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// User-defined destination (and option) support for CUPS.
//
// Copyright © 2021-2024 by OpenPrinting.
// Copyright © 2021-2025 by OpenPrinting.
// Copyright © 2007-2019 by Apple Inc.
// Copyright © 1997-2007 by Easy Software Products.
//
Expand Down Expand Up @@ -1074,7 +1074,8 @@ _cupsGetDests(http_t *http, // I - Connection to server or `CUPS_HTTP_DEF
ipp_t *request, // IPP Request
*response; // IPP Response
ipp_attribute_t *attr; // Current attribute
const char *printer_name; // printer-name attribute
const char *printer_name, // printer-name attribute
*printer_location; // printer-location attribute
char uri[1024]; // printer-uri value
size_t num_options; // Number of options
cups_option_t *options; // Options
Expand All @@ -1084,6 +1085,7 @@ _cupsGetDests(http_t *http, // I - Connection to server or `CUPS_HTTP_DEF
char optname[1024], // Option name
value[2048], // Option value
*ptr; // Pointer into name/value
_cups_globals_t *cg = _cupsGlobals(); // Pointer to library globals
static const char * const pattrs[] = // Attributes we're interested in
{
"auth-info-required",
Expand Down Expand Up @@ -1166,9 +1168,10 @@ _cupsGetDests(http_t *http, // I - Connection to server or `CUPS_HTTP_DEF
break;

// Pull the needed attributes from this printer...
printer_name = NULL;
num_options = 0;
options = NULL;
printer_name = NULL;
printer_location = NULL;
num_options = 0;
options = NULL;

for (; attr && attr->group_tag == IPP_TAG_PRINTER; attr = attr->next)
{
Expand Down Expand Up @@ -1208,12 +1211,18 @@ _cupsGetDests(http_t *http, // I - Connection to server or `CUPS_HTTP_DEF
!strcmp(attr->name, "printer-state-reasons") ||
!strcmp(attr->name, "printer-type") ||
!strcmp(attr->name, "printer-is-accepting-jobs") ||
!strcmp(attr->name, "printer-location") ||
!strcmp(attr->name, "printer-uri-supported"))
{
// Add a printer description attribute...
num_options = cupsAddOption(attr->name, cups_make_string(attr, value, sizeof(value)), num_options, &options);
}
else if (!strcmp(attr->name, "printer-location"))
{
// Add a printer description attribute...
printer_location = ippGetString(attr, 0, NULL);

num_options = cupsAddOption(attr->name, printer_location, num_options, &options);
}
#ifdef __APPLE__
else if (!strcmp(attr->name, "media-supported") && media_default[0])
{
Expand Down Expand Up @@ -1247,7 +1256,20 @@ _cupsGetDests(http_t *http, // I - Connection to server or `CUPS_HTTP_DEF
}

// See if we have everything needed...
if (!printer_name)
if (printer_location && cg->filter_location_array != NULL)
{
// Look for a matching printer location...
if (!cupsArrayFind(cg->filter_location_array, (void *)printer_location))
printer_location = NULL;
}
else if (printer_location && cg->filter_location_regex != NULL)
{
// Match the printer location against a regular expression...
if (regexec(cg->filter_location_regex, printer_location, 0, NULL, 0))
printer_location = NULL;
}

if (!printer_name || !printer_location)
{
cupsFreeOptions(num_options, options);

Expand Down Expand Up @@ -2637,6 +2659,7 @@ cups_enum_dests(
size_t i, j, k, // Looping vars
num_dests, // Number of destinations
num_devices; // Number of devices
const char *domain; // Current domain
cups_dest_t *dests = NULL, // Destinations
*dest; // Current destination
cups_option_t *option; // Current option
Expand All @@ -2663,6 +2686,10 @@ cups_enum_dests(
return (false);
}

// Load the client.conf files...
if (!cg->client_conf_loaded)
_cupsSetDefaults();

// Load the /etc/cups/lpoptions and ~/.cups/lpoptions files...
memset(&data, 0, sizeof(data));

Expand Down Expand Up @@ -2706,16 +2733,16 @@ cups_enum_dests(
// Get ready to enumerate...
cupsRWInit(&data.rwlock);

data.type = type;
data.mask = mask;
data.type = type | cg->filter_type;
data.mask = mask | cg->filter_type_mask;
data.cb = cb;
data.user_data = user_data;
data.devices = cupsArrayNew((cups_array_cb_t)cups_dnssd_compare_devices, NULL, NULL, 0, NULL, (cups_afree_cb_t)cups_dnssd_free_device);

if (!(mask & CUPS_PTYPE_DISCOVERED) || !(type & CUPS_PTYPE_DISCOVERED))
{
// Get the list of local printers and pass them to the callback function...
num_dests = _cupsGetDests(http, IPP_OP_CUPS_GET_PRINTERS, NULL, &dests, type, mask);
num_dests = _cupsGetDests(http, IPP_OP_CUPS_GET_PRINTERS, NULL, &dests, data.type, data.mask);

if (data.def_name[0])
{
Expand Down Expand Up @@ -2796,6 +2823,9 @@ cups_enum_dests(
if ((mask & CUPS_PTYPE_DISCOVERED) && !(type & CUPS_PTYPE_DISCOVERED))
goto enum_finished;

if (cg->browse_domains && cupsArrayGetCount(cg->browse_domains) == 0)
goto enum_finished;

// Get DNS-SD printers...
if ((dnssd = cupsDNSSDNew(dnssd_error_cb, NULL)) == NULL)
{
Expand All @@ -2807,26 +2837,62 @@ cups_enum_dests(
return (false);
}

if (!cupsDNSSDBrowseNew(dnssd, CUPS_DNSSD_IF_INDEX_ANY, "_ipp._tcp", /*domain*/NULL, cups_dest_browse_cb, &data))
if (cg->browse_domains)
{
DEBUG_puts("1cups_enum_dests: Unable to create IPP browser, returning 0.");
cupsDNSSDDelete(dnssd);
// Browse listed domains
for (domain = (char *)cupsArrayGetFirst(cg->browse_domains); domain; domain = (char *)cupsArrayGetNext(cg->browse_domains))
{
// Drop leading '.' in domain name...
if (*domain == '.')
domain ++;

cupsFreeDests(data.num_dests, data.dests);
cupsArrayDelete(data.devices);
if (!cupsDNSSDBrowseNew(dnssd, CUPS_DNSSD_IF_INDEX_ANY, "_ipp._tcp", domain, cups_dest_browse_cb, &data))
{
DEBUG_puts("1cups_enum_dests: Unable to create IPP browser, returning 0.");
cupsDNSSDDelete(dnssd);

return (false);
}
cupsFreeDests(data.num_dests, data.dests);
cupsArrayDelete(data.devices);

return (false);
}

if (!cupsDNSSDBrowseNew(dnssd, CUPS_DNSSD_IF_INDEX_ANY, "_ipps._tcp", /*domain*/NULL, cups_dest_browse_cb, &data))
if (!cupsDNSSDBrowseNew(dnssd, CUPS_DNSSD_IF_INDEX_ANY, "_ipps._tcp", domain, cups_dest_browse_cb, &data))
{
DEBUG_puts("1cups_enum_dests: Unable to create IPPS browser, returning 0.");
cupsDNSSDDelete(dnssd);

cupsFreeDests(data.num_dests, data.dests);
cupsArrayDelete(data.devices);

return (false);
}
}
}
else
{
DEBUG_puts("1cups_enum_dests: Unable to create IPPS browser, returning 0.");
cupsDNSSDDelete(dnssd);
// Browse all domains...
if (!cupsDNSSDBrowseNew(dnssd, CUPS_DNSSD_IF_INDEX_ANY, "_ipp._tcp", /*domain*/NULL, cups_dest_browse_cb, &data))
{
DEBUG_puts("1cups_enum_dests: Unable to create IPP browser, returning 0.");
cupsDNSSDDelete(dnssd);

cupsFreeDests(data.num_dests, data.dests);
cupsArrayDelete(data.devices);
cupsFreeDests(data.num_dests, data.dests);
cupsArrayDelete(data.devices);

return (false);
return (false);
}

if (!cupsDNSSDBrowseNew(dnssd, CUPS_DNSSD_IF_INDEX_ANY, "_ipps._tcp", /*domain*/NULL, cups_dest_browse_cb, &data))
{
DEBUG_puts("1cups_enum_dests: Unable to create IPPS browser, returning 0.");
cupsDNSSDDelete(dnssd);

cupsFreeDests(data.num_dests, data.dests);
cupsArrayDelete(data.devices);

return (false);
}
}

if (msec < 0)
Expand Down Expand Up @@ -2870,14 +2936,34 @@ cups_enum_dests(
}
else if (device->query && device->state == _CUPS_DNSSD_PENDING)
{
const char *location = cupsGetOption("printer-location", device->dest.num_options, device->dest.options);
// Printer location, if any

completed ++;

DEBUG_printf("1cups_enum_dests: Query for \"%s\" is complete.", device->fullname);

if ((device->type & mask) == type)
if (location && cg->filter_location_array)
{
if (!cupsArrayFind(cg->filter_location_array, (void *)location))
device->state = _CUPS_DNSSD_INCOMPATIBLE;
}
else if (location && cg->filter_location_regex)
{
if (regexec(cg->filter_location_regex, location, 0, NULL, 0))
device->state = _CUPS_DNSSD_INCOMPATIBLE;
}

if ((device->type & mask) != type)
device->state = _CUPS_DNSSD_INCOMPATIBLE;

if (device->state == _CUPS_DNSSD_PENDING)
{
// Device is OK...
cups_dest_t *user_dest; // Destination from lpoptions

DEBUG_printf("2cups_enum_dests: Doing callback for '%s'.", device->fullname);

dest = &device->dest;

if ((user_dest = cupsGetDest(dest->name, dest->instance, data.num_dests, data.dests)) != NULL)
Expand Down Expand Up @@ -2919,9 +3005,14 @@ cups_enum_dests(
break;
}
}
}

device->state = _CUPS_DNSSD_ACTIVE;
device->state = _CUPS_DNSSD_ACTIVE;
}
else
{
// Filter this one out...
DEBUG_printf("2cups_enum_dests: Filtered out '%s'.", device->fullname);
}
}
}

Expand Down
10 changes: 9 additions & 1 deletion cups/globals.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// Global variable access routines for CUPS.
//
// Copyright © 2021-2022 by OpenPrinting.
// Copyright © 2021-2025 by OpenPrinting.
// Copyright © 2007-2019 by Apple Inc.
// Copyright © 1997-2007 by Easy Software Products, all rights reserved.
//
Expand Down Expand Up @@ -379,6 +379,14 @@ cups_globals_free(_cups_globals_t *cg) // I - Pointer to global data
cupsFileClose(cg->stdio_files[1]);
cupsFileClose(cg->stdio_files[2]);

cupsArrayDelete(cg->browse_domains);
cupsArrayDelete(cg->filter_location_array);
if (cg->filter_location_regex)
{
regfree(cg->filter_location_regex);
free(cg->filter_location_regex);
}

free(cg->userconfig);
free(cg->raster_error.start);
free(cg);
Expand Down
4 changes: 2 additions & 2 deletions cups/ipp-support.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// Internet Printing Protocol support functions for CUPS.
//
// Copyright © 2022-2024 by OpenPrinting.
// Copyright © 2022-2025 by OpenPrinting.
// Copyright © 2007-2018 by Apple Inc.
// Copyright © 1997-2007 by Easy Software Products, all rights reserved.
//
Expand Down Expand Up @@ -2365,7 +2365,7 @@ ippGetPort(void)

DEBUG_puts("ippPort()");

if (!cg->ipp_port)
if (!cg->client_conf_loaded)
_cupsSetDefaults();

DEBUG_printf("1ippPort: Returning %d...", cg->ipp_port);
Expand Down
4 changes: 2 additions & 2 deletions cups/ipp.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// Internet Printing Protocol functions for CUPS.
//
// Copyright © 2021-2024 by OpenPrinting.
// Copyright © 2021-2025 by OpenPrinting.
// Copyright © 2007-2021 by Apple Inc.
// Copyright © 1997-2007 by Easy Software Products, all rights reserved.
//
Expand Down Expand Up @@ -2344,7 +2344,7 @@ ippNew(void)
// Set default version - usually 2.0...
DEBUG_printf("4debug_alloc: %p IPP message", (void *)temp);

if (cg->server_version == 0)
if (!cg->client_conf_loaded)
_cupsSetDefaults();

temp->request.any.version[0] = (ipp_uchar_t)(cg->server_version / 10);
Expand Down
4 changes: 2 additions & 2 deletions cups/tls-gnutls.c
Original file line number Diff line number Diff line change
Expand Up @@ -901,7 +901,7 @@ cupsGetCredentialsTrust(
return (HTTP_TRUST_UNKNOWN);
}

if (cg->any_root < 0)
if (!cg->client_conf_loaded)
{
_cupsSetDefaults();
gnutls_load_crl();
Expand Down Expand Up @@ -1616,7 +1616,7 @@ _httpTLSStart(http_t *http) // I - Connection to server

DEBUG_printf("3_httpTLSStart(http=%p)", http);

if (tls_options < 0)
if (!cg->client_conf_loaded)
{
DEBUG_puts("4_httpTLSStart: Setting defaults.");
_cupsSetDefaults();
Expand Down
Loading

0 comments on commit 2371ba8

Please sign in to comment.