#include "cups-private.h"
#define _CUPS_MEDIA_READY_TTL 30
static void cups_add_dconstres(cups_array_t *a, ipp_t *collection);
static int cups_compare_dconstres(_cups_dconstres_t *a,
_cups_dconstres_t *b);
static int cups_compare_media_db(_cups_media_db_t *a,
_cups_media_db_t *b);
static _cups_media_db_t *cups_copy_media_db(_cups_media_db_t *mdb);
static void cups_create_cached(http_t *http, cups_dinfo_t *dinfo,
unsigned flags);
static void cups_create_constraints(cups_dinfo_t *dinfo);
static void cups_create_defaults(cups_dinfo_t *dinfo);
static void cups_create_media_db(cups_dinfo_t *dinfo,
unsigned flags);
static void cups_free_media_db(_cups_media_db_t *mdb);
static int cups_get_media_db(http_t *http, cups_dinfo_t *dinfo,
pwg_media_t *pwg, unsigned flags,
cups_size_t *size);
static int cups_is_close_media_db(_cups_media_db_t *a,
_cups_media_db_t *b);
static cups_array_t *cups_test_constraints(cups_dinfo_t *dinfo,
const char *new_option,
const char *new_value,
int num_options,
cups_option_t *options,
int *num_conflicts,
cups_option_t **conflicts);
static void cups_update_ready(http_t *http, cups_dinfo_t *dinfo);
int
cupsCheckDestSupported(
http_t *http,
cups_dest_t *dest,
cups_dinfo_t *dinfo,
const char *option,
const char *value)
{
int i;
char temp[1024];
int int_value;
int xres_value,
yres_value;
ipp_res_t units_value;
ipp_attribute_t *attr;
_ipp_value_t *attrval;
if (!http || !dest || !dinfo || !option || !value)
return (0);
if (strstr(option, "-supported"))
attr = ippFindAttribute(dinfo->attrs, option, IPP_TAG_ZERO);
else
{
snprintf(temp, sizeof(temp), "%s-supported", option);
attr = ippFindAttribute(dinfo->attrs, temp, IPP_TAG_ZERO);
}
if (!attr)
return (0);
if (!strcmp(option, "media") && !strncmp(value, "custom_", 7))
{
pwg_media_t *pwg;
int min_width,
min_length,
max_width,
max_length;
min_width = min_length = INT_MAX;
max_width = max_length = 0;
for (i = attr->num_values, attrval = attr->values;
i > 0;
i --, attrval ++)
{
if (!strncmp(attrval->string.text, "custom_min_", 11) &&
(pwg = pwgMediaForPWG(attrval->string.text)) != NULL)
{
min_width = pwg->width;
min_length = pwg->length;
}
else if (!strncmp(attrval->string.text, "custom_max_", 11) &&
(pwg = pwgMediaForPWG(attrval->string.text)) != NULL)
{
max_width = pwg->width;
max_length = pwg->length;
}
}
if (min_width < INT_MAX && max_width > 0 &&
(pwg = pwgMediaForPWG(value)) != NULL &&
pwg->width >= min_width && pwg->width <= max_width &&
pwg->length >= min_length && pwg->length <= max_length)
return (1);
}
else
{
switch (attr->value_tag)
{
case IPP_TAG_INTEGER :
case IPP_TAG_ENUM :
int_value = atoi(value);
for (i = 0; i < attr->num_values; i ++)
if (attr->values[i].integer == int_value)
return (1);
break;
case IPP_TAG_BOOLEAN :
return (attr->values[0].boolean);
case IPP_TAG_RANGE :
int_value = atoi(value);
for (i = 0; i < attr->num_values; i ++)
if (int_value >= attr->values[i].range.lower &&
int_value <= attr->values[i].range.upper)
return (1);
break;
case IPP_TAG_RESOLUTION :
if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
{
if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
return (0);
yres_value = xres_value;
}
if (!strcmp(temp, "dpi"))
units_value = IPP_RES_PER_INCH;
else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm"))
units_value = IPP_RES_PER_CM;
else
return (0);
for (i = attr->num_values, attrval = attr->values;
i > 0;
i --, attrval ++)
{
if (attrval->resolution.xres == xres_value &&
attrval->resolution.yres == yres_value &&
attrval->resolution.units == units_value)
return (1);
}
break;
case IPP_TAG_TEXT :
case IPP_TAG_NAME :
case IPP_TAG_KEYWORD :
case IPP_TAG_CHARSET :
case IPP_TAG_URI :
case IPP_TAG_URISCHEME :
case IPP_TAG_MIMETYPE :
case IPP_TAG_LANGUAGE :
case IPP_TAG_TEXTLANG :
case IPP_TAG_NAMELANG :
for (i = 0; i < attr->num_values; i ++)
if (!strcmp(attr->values[i].string.text, value))
return (1);
break;
default :
break;
}
}
return (0);
}
int
cupsCopyDestConflicts(
http_t *http,
cups_dest_t *dest,
cups_dinfo_t *dinfo,
int num_options,
cups_option_t *options,
const char *new_option,
const char *new_value,
int *num_conflicts,
cups_option_t **conflicts,
int *num_resolved,
cups_option_t **resolved)
{
int i,
have_conflicts = 0,
changed,
tries,
num_myconf = 0,
num_myres = 0;
cups_option_t *myconf = NULL,
*myres = NULL,
*myoption,
*option;
cups_array_t *active,
*pass = NULL,
*resolvers = NULL,
*test;
_cups_dconstres_t *c,
*r;
ipp_attribute_t *attr;
char value[2048];
const char *myvalue;
if (num_conflicts)
*num_conflicts = 0;
if (conflicts)
*conflicts = NULL;
if (num_resolved)
*num_resolved = 0;
if (resolved)
*resolved = NULL;
if (!http || !dest || !dinfo ||
(num_conflicts != NULL) != (conflicts != NULL) ||
(num_resolved != NULL) != (resolved != NULL))
return (0);
if (!dinfo->constraints)
cups_create_constraints(dinfo);
if (cupsArrayCount(dinfo->constraints) == 0)
return (0);
if (!dinfo->num_defaults)
cups_create_defaults(dinfo);
if (num_resolved)
{
for (i = num_options, option = options; i > 0; i --, option ++)
num_myres = cupsAddOption(option->name, option->value, num_myres, &myres);
if (new_option && new_value)
num_myres = cupsAddOption(new_option, new_value, num_myres, &myres);
}
else
{
num_myres = num_options;
myres = options;
}
if (num_resolved)
pass = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL);
for (tries = 0; tries < 100; tries ++)
{
if (num_conflicts || num_resolved)
{
cupsFreeOptions(num_myconf, myconf);
num_myconf = 0;
myconf = NULL;
active = cups_test_constraints(dinfo, new_option, new_value,
num_myres, myres, &num_myconf,
&myconf);
}
else
active = cups_test_constraints(dinfo, new_option, new_value, num_myres,
myres, NULL, NULL);
have_conflicts = (active != NULL);
if (!active || !num_resolved)
break;
if (!resolvers)
resolvers = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL);
for (c = (_cups_dconstres_t *)cupsArrayFirst(active), changed = 0;
c;
c = (_cups_dconstres_t *)cupsArrayNext(active))
{
if (cupsArrayFind(pass, c))
continue;
if (cupsArrayFind(resolvers, c))
{
DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.",
c->name));
have_conflicts = -1;
goto cleanup;
}
if ((r = cupsArrayFind(dinfo->resolvers, c)) == NULL)
{
DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.",
c->name));
have_conflicts = -1;
goto cleanup;
}
cupsArrayAdd(pass, r);
cupsArrayAdd(resolvers, r);
for (attr = ippFirstAttribute(r->collection);
attr;
attr = ippNextAttribute(r->collection))
{
if (new_option && !strcmp(attr->name, new_option))
continue;
if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value))
continue;
if ((test = cups_test_constraints(dinfo, attr->name, value, num_myres,
myres, NULL, NULL)) == NULL)
{
changed = 1;
}
else
cupsArrayDelete(test);
num_myres = cupsAddOption(attr->name, value, num_myres, &myres);
}
}
if (!changed)
{
DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints.");
have_conflicts = -1;
goto cleanup;
}
cupsArrayClear(pass);
cupsArrayDelete(active);
active = NULL;
}
if (tries >= 100)
{
DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries.");
have_conflicts = -1;
goto cleanup;
}
if (num_resolved)
{
for (i = num_myres, myoption = myres; i > 0; i --, myoption ++)
{
if ((myvalue = cupsGetOption(myoption->name, num_options,
options)) == NULL ||
strcmp(myvalue, myoption->value))
{
if (new_option && !strcmp(new_option, myoption->name) &&
new_value && !strcmp(new_value, myoption->value))
continue;
*num_resolved = cupsAddOption(myoption->name, myoption->value,
*num_resolved, resolved);
}
}
}
cleanup:
cupsArrayDelete(active);
cupsArrayDelete(pass);
cupsArrayDelete(resolvers);
if (num_resolved)
{
cupsFreeOptions(num_myres, myres);
}
if (num_conflicts)
{
*num_conflicts = num_myconf;
*conflicts = myconf;
}
else
{
cupsFreeOptions(num_myconf, myconf);
}
return (have_conflicts);
}
cups_dinfo_t *
cupsCopyDestInfo(
http_t *http,
cups_dest_t *dest)
{
cups_dinfo_t *dinfo;
ipp_t *request,
*response;
int tries,
delay,
prev_delay;
const char *uri;
char resource[1024];
int version;
ipp_status_t status;
static const char * const requested_attrs[] =
{
"job-template",
"media-col-database",
"printer-description"
};
DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", http, dest,
dest ? dest->name : ""));
if (!http || !dest)
return (NULL);
if ((uri = _cupsGetDestResource(dest, resource, sizeof(resource))) == NULL)
return (NULL);
delay = 1;
prev_delay = 1;
tries = 0;
version = 20;
do
{
request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
"requesting-user-name", NULL, cupsUser());
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
"requested-attributes",
(int)(sizeof(requested_attrs) / sizeof(requested_attrs[0])),
NULL, requested_attrs);
response = cupsDoRequest(http, request, resource);
status = cupsLastError();
if (status > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
{
DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' "
"returned %s (%s)", dest->name, ippErrorString(status),
cupsLastErrorString()));
ippDelete(response);
response = NULL;
if (status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED && version > 11)
version = 11;
else if (status == IPP_STATUS_ERROR_BUSY)
{
sleep(delay);
delay = _cupsNextDelay(delay, &prev_delay);
}
else
return (NULL);
}
tries ++;
}
while (!response && tries < 10);
if (!response)
return (NULL);
if ((dinfo = calloc(1, sizeof(cups_dinfo_t))) == NULL)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
ippDelete(response);
return (NULL);
}
dinfo->version = version;
dinfo->uri = uri;
dinfo->resource = _cupsStrAlloc(resource);
dinfo->attrs = response;
return (dinfo);
}
ipp_attribute_t *
cupsFindDestDefault(
http_t *http,
cups_dest_t *dest,
cups_dinfo_t *dinfo,
const char *option)
{
char name[IPP_MAX_NAME];
if (!http || !dest || !dinfo || !option)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
return (NULL);
}
snprintf(name, sizeof(name), "%s-default", option);
return (ippFindAttribute(dinfo->attrs, name, IPP_TAG_ZERO));
}
ipp_attribute_t *
cupsFindDestReady(
http_t *http,
cups_dest_t *dest,
cups_dinfo_t *dinfo,
const char *option)
{
char name[IPP_MAX_NAME];
if (!http || !dest || !dinfo || !option)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
return (NULL);
}
cups_update_ready(http, dinfo);
snprintf(name, sizeof(name), "%s-ready", option);
return (ippFindAttribute(dinfo->ready_attrs, name, IPP_TAG_ZERO));
}
ipp_attribute_t *
cupsFindDestSupported(
http_t *http,
cups_dest_t *dest,
cups_dinfo_t *dinfo,
const char *option)
{
char name[IPP_MAX_NAME];
if (!http || !dest || !dinfo || !option)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
return (NULL);
}
snprintf(name, sizeof(name), "%s-supported", option);
return (ippFindAttribute(dinfo->attrs, name, IPP_TAG_ZERO));
}
void
cupsFreeDestInfo(cups_dinfo_t *dinfo)
{
if (!dinfo)
return;
_cupsStrFree(dinfo->resource);
cupsArrayDelete(dinfo->constraints);
cupsArrayDelete(dinfo->resolvers);
cupsArrayDelete(dinfo->localizations);
cupsArrayDelete(dinfo->media_db);
cupsArrayDelete(dinfo->cached_db);
ippDelete(dinfo->ready_attrs);
cupsArrayDelete(dinfo->ready_db);
ippDelete(dinfo->attrs);
free(dinfo);
}
int
cupsGetDestMediaByIndex(
http_t *http,
cups_dest_t *dest,
cups_dinfo_t *dinfo,
int n,
unsigned flags,
cups_size_t *size)
{
cups_size_t *nsize;
if (size)
memset(size, 0, sizeof(cups_size_t));
if (!http || !dest || !dinfo || n < 0 || !size)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
return (0);
}
if (flags & CUPS_MEDIA_FLAGS_READY)
cups_update_ready(http, dinfo);
if (!dinfo->cached_db || dinfo->cached_flags != flags)
cups_create_cached(http, dinfo, flags);
if ((nsize = (cups_size_t *)cupsArrayIndex(dinfo->cached_db, n)) == NULL)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
return (0);
}
memcpy(size, nsize, sizeof(cups_size_t));
return (1);
}
int
cupsGetDestMediaByName(
http_t *http,
cups_dest_t *dest,
cups_dinfo_t *dinfo,
const char *media,
unsigned flags,
cups_size_t *size)
{
pwg_media_t *pwg;
if (size)
memset(size, 0, sizeof(cups_size_t));
if (!http || !dest || !dinfo || !media || !size)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
return (0);
}
if ((pwg = pwgMediaForPWG(media)) == NULL)
if ((pwg = pwgMediaForLegacy(media)) == NULL)
{
DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media));
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown media size name."), 1);
return (0);
}
return (cups_get_media_db(http, dinfo, pwg, flags, size));
}
int
cupsGetDestMediaBySize(
http_t *http,
cups_dest_t *dest,
cups_dinfo_t *dinfo,
int width,
int length,
unsigned flags,
cups_size_t *size)
{
pwg_media_t *pwg;
if (size)
memset(size, 0, sizeof(cups_size_t));
if (!http || !dest || !dinfo || width <= 0 || length <= 0 || !size)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
return (0);
}
if ((pwg = pwgMediaForSize(width, length)) == NULL)
{
DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width,
length));
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid media size."), 1);
return (0);
}
return (cups_get_media_db(http, dinfo, pwg, flags, size));
}
int
cupsGetDestMediaCount(
http_t *http,
cups_dest_t *dest,
cups_dinfo_t *dinfo,
unsigned flags)
{
if (!http || !dest || !dinfo)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
return (0);
}
if (flags & CUPS_MEDIA_FLAGS_READY)
cups_update_ready(http, dinfo);
if (!dinfo->cached_db || dinfo->cached_flags != flags)
cups_create_cached(http, dinfo, flags);
return (cupsArrayCount(dinfo->cached_db));
}
int
cupsGetDestMediaDefault(
http_t *http,
cups_dest_t *dest,
cups_dinfo_t *dinfo,
unsigned flags,
cups_size_t *size)
{
const char *media;
if (size)
memset(size, 0, sizeof(cups_size_t));
if (!http || !dest || !dinfo || !size)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
return (0);
}
if ((media = cupsGetOption("media", dest->num_options,
dest->options)) == NULL)
media = "na_letter_8.5x11in";
if (cupsGetDestMediaByName(http, dest, dinfo, media, flags, size))
return (1);
if (strcmp(media, "na_letter_8.5x11in") &&
cupsGetDestMediaByName(http, dest, dinfo, "iso_a4_210x297mm", flags,
size))
return (1);
if (strcmp(media, "iso_a4_210x297mm") &&
cupsGetDestMediaByName(http, dest, dinfo, "na_letter_8.5x11in", flags,
size))
return (1);
if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) &&
cupsGetDestMediaByName(http, dest, dinfo, "na_index_4x6in", flags, size))
return (1);
return (cupsGetDestMediaByIndex(http, dest, dinfo, flags, 0, size));
}
static void
cups_add_dconstres(
cups_array_t *a,
ipp_t *collection)
{
ipp_attribute_t *attr;
_cups_dconstres_t *temp;
if ((attr = ippFindAttribute(collection, "resolver-name",
IPP_TAG_NAME)) == NULL)
return;
if ((temp = calloc(1, sizeof(_cups_dconstres_t))) == NULL)
return;
temp->name = attr->values[0].string.text;
temp->collection = collection;
cupsArrayAdd(a, temp);
}
static int
cups_compare_dconstres(
_cups_dconstres_t *a,
_cups_dconstres_t *b)
{
return (strcmp(a->name, b->name));
}
static int
cups_compare_media_db(
_cups_media_db_t *a,
_cups_media_db_t *b)
{
int result;
if ((result = a->width - b->width) == 0)
result = a->length - b->length;
return (result);
}
static _cups_media_db_t *
cups_copy_media_db(
_cups_media_db_t *mdb)
{
_cups_media_db_t *temp;
if ((temp = calloc(1, sizeof(_cups_media_db_t))) == NULL)
return (NULL);
if (mdb->color)
temp->color = _cupsStrAlloc(mdb->color);
if (mdb->key)
temp->key = _cupsStrAlloc(mdb->key);
if (mdb->info)
temp->info = _cupsStrAlloc(mdb->info);
if (mdb->size_name)
temp->size_name = _cupsStrAlloc(mdb->size_name);
if (mdb->source)
temp->source = _cupsStrAlloc(mdb->source);
if (mdb->type)
temp->type = _cupsStrAlloc(mdb->type);
temp->width = mdb->width;
temp->length = mdb->length;
temp->bottom = mdb->bottom;
temp->left = mdb->left;
temp->right = mdb->right;
temp->top = mdb->top;
return (temp);
}
static void
cups_create_cached(http_t *http,
cups_dinfo_t *dinfo,
unsigned flags)
{
cups_array_t *db;
_cups_media_db_t *mdb,
*first;
if (dinfo->cached_db)
cupsArrayDelete(dinfo->cached_db);
dinfo->cached_db = cupsArrayNew(NULL, NULL);
dinfo->cached_flags = flags;
if (flags & CUPS_MEDIA_FLAGS_READY)
{
cups_update_ready(http, dinfo);
db = dinfo->ready_db;
}
else
{
if (!dinfo->media_db)
cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_DEFAULT);
db = dinfo->media_db;
}
for (mdb = (_cups_media_db_t *)cupsArrayFirst(db), first = mdb;
mdb;
mdb = (_cups_media_db_t *)cupsArrayNext(db))
{
if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
{
if (!mdb->left && !mdb->right && !mdb->top && !mdb->bottom)
cupsArrayAdd(dinfo->cached_db, mdb);
}
else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
{
if (first->width != mdb->width || first->length != mdb->length)
{
cupsArrayAdd(dinfo->cached_db, first);
first = mdb;
}
else if (mdb->left >= first->left && mdb->right >= first->right &&
mdb->top >= first->top && mdb->bottom >= first->bottom)
first = mdb;
}
}
if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
cupsArrayAdd(dinfo->cached_db, first);
}
static void
cups_create_constraints(
cups_dinfo_t *dinfo)
{
int i;
ipp_attribute_t *attr;
_ipp_value_t *val;
dinfo->constraints = cupsArrayNew3(NULL, NULL, NULL, 0, NULL,
(cups_afree_func_t)free);
dinfo->resolvers = cupsArrayNew3((cups_array_func_t)cups_compare_dconstres,
NULL, NULL, 0, NULL,
(cups_afree_func_t)free);
if ((attr = ippFindAttribute(dinfo->attrs, "job-constraints-supported",
IPP_TAG_BEGIN_COLLECTION)) != NULL)
{
for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
cups_add_dconstres(dinfo->constraints, val->collection);
}
if ((attr = ippFindAttribute(dinfo->attrs, "job-resolvers-supported",
IPP_TAG_BEGIN_COLLECTION)) != NULL)
{
for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
cups_add_dconstres(dinfo->resolvers, val->collection);
}
}
static void
cups_create_defaults(
cups_dinfo_t *dinfo)
{
ipp_attribute_t *attr;
char name[IPP_MAX_NAME + 1],
*nameptr,
value[2048];
for (attr = ippFirstAttribute(dinfo->attrs);
attr;
attr = ippNextAttribute(dinfo->attrs))
{
if (!attr->name || attr->group_tag != IPP_TAG_PRINTER)
continue;
if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
continue;
if ((nameptr = attr->name + strlen(attr->name) - 8) <= attr->name ||
strcmp(nameptr, "-default"))
continue;
strlcpy(name, attr->name, sizeof(name));
if ((nameptr = name + strlen(name) - 8) <= name ||
strcmp(nameptr, "-default"))
continue;
*nameptr = '\0';
if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value))
continue;
dinfo->num_defaults = cupsAddOption(name, value, dinfo->num_defaults,
&dinfo->defaults);
}
}
static void
cups_create_media_db(
cups_dinfo_t *dinfo,
unsigned flags)
{
int i;
_ipp_value_t *val;
ipp_attribute_t *media_col_db,
*media_attr,
*x_dimension,
*y_dimension;
pwg_media_t *pwg;
cups_array_t *db;
_cups_media_db_t mdb;
db = cupsArrayNew3((cups_array_func_t)cups_compare_media_db,
NULL, NULL, 0,
(cups_acopy_func_t)cups_copy_media_db,
(cups_afree_func_t)cups_free_media_db);
if (flags == CUPS_MEDIA_FLAGS_READY)
{
dinfo->ready_db = db;
media_col_db = ippFindAttribute(dinfo->ready_attrs, "media-col-ready",
IPP_TAG_BEGIN_COLLECTION);
media_attr = ippFindAttribute(dinfo->ready_attrs, "media-ready",
IPP_TAG_ZERO);
}
else
{
dinfo->media_db = db;
dinfo->min_size.width = INT_MAX;
dinfo->min_size.length = INT_MAX;
dinfo->max_size.width = 0;
dinfo->max_size.length = 0;
media_col_db = ippFindAttribute(dinfo->attrs, "media-col-database",
IPP_TAG_BEGIN_COLLECTION);
media_attr = ippFindAttribute(dinfo->attrs, "media-supported",
IPP_TAG_ZERO);
}
if (media_col_db)
{
_ipp_value_t *custom = NULL;
for (i = media_col_db->num_values, val = media_col_db->values;
i > 0;
i --, val ++)
{
memset(&mdb, 0, sizeof(mdb));
if ((media_attr = ippFindAttribute(val->collection, "media-size",
IPP_TAG_BEGIN_COLLECTION)) != NULL)
{
ipp_t *media_size = media_attr->values[0].collection;
if ((x_dimension = ippFindAttribute(media_size, "x-dimension",
IPP_TAG_INTEGER)) != NULL &&
(y_dimension = ippFindAttribute(media_size, "y-dimension",
IPP_TAG_INTEGER)) != NULL)
{
mdb.width = x_dimension->values[0].integer;
mdb.length = y_dimension->values[0].integer;
}
else if ((x_dimension = ippFindAttribute(media_size, "x-dimension",
IPP_TAG_INTEGER)) != NULL &&
(y_dimension = ippFindAttribute(media_size, "y-dimension",
IPP_TAG_RANGE)) != NULL)
{
mdb.width = x_dimension->values[0].integer;
mdb.length = y_dimension->values[0].range.upper;
}
else if (flags != CUPS_MEDIA_FLAGS_READY &&
(x_dimension = ippFindAttribute(media_size, "x-dimension",
IPP_TAG_RANGE)) != NULL &&
(y_dimension = ippFindAttribute(media_size, "y-dimension",
IPP_TAG_RANGE)) != NULL)
{
custom = val;
dinfo->min_size.width = x_dimension->values[0].range.lower;
dinfo->min_size.length = y_dimension->values[0].range.lower;
dinfo->min_size.left =
dinfo->min_size.right = 635;
dinfo->min_size.top =
dinfo->min_size.bottom = 1270;
dinfo->max_size.width = x_dimension->values[0].range.upper;
dinfo->max_size.length = y_dimension->values[0].range.upper;
dinfo->max_size.left =
dinfo->max_size.right = 635;
dinfo->max_size.top =
dinfo->max_size.bottom = 1270;
continue;
}
}
if ((media_attr = ippFindAttribute(val->collection, "media-color",
IPP_TAG_ZERO)) != NULL &&
(media_attr->value_tag == IPP_TAG_NAME ||
media_attr->value_tag == IPP_TAG_NAMELANG ||
media_attr->value_tag == IPP_TAG_KEYWORD))
mdb.color = media_attr->values[0].string.text;
if ((media_attr = ippFindAttribute(val->collection, "media-info",
IPP_TAG_TEXT)) != NULL)
mdb.info = media_attr->values[0].string.text;
if ((media_attr = ippFindAttribute(val->collection, "media-key",
IPP_TAG_ZERO)) != NULL &&
(media_attr->value_tag == IPP_TAG_NAME ||
media_attr->value_tag == IPP_TAG_NAMELANG ||
media_attr->value_tag == IPP_TAG_KEYWORD))
mdb.key = media_attr->values[0].string.text;
if ((media_attr = ippFindAttribute(val->collection, "media-size-name",
IPP_TAG_ZERO)) != NULL &&
(media_attr->value_tag == IPP_TAG_NAME ||
media_attr->value_tag == IPP_TAG_NAMELANG ||
media_attr->value_tag == IPP_TAG_KEYWORD))
mdb.size_name = media_attr->values[0].string.text;
if ((media_attr = ippFindAttribute(val->collection, "media-source",
IPP_TAG_ZERO)) != NULL &&
(media_attr->value_tag == IPP_TAG_NAME ||
media_attr->value_tag == IPP_TAG_NAMELANG ||
media_attr->value_tag == IPP_TAG_KEYWORD))
mdb.source = media_attr->values[0].string.text;
if ((media_attr = ippFindAttribute(val->collection, "media-type",
IPP_TAG_ZERO)) != NULL &&
(media_attr->value_tag == IPP_TAG_NAME ||
media_attr->value_tag == IPP_TAG_NAMELANG ||
media_attr->value_tag == IPP_TAG_KEYWORD))
mdb.type = media_attr->values[0].string.text;
if ((media_attr = ippFindAttribute(val->collection, "media-bottom-margin",
IPP_TAG_INTEGER)) != NULL)
mdb.bottom = media_attr->values[0].integer;
if ((media_attr = ippFindAttribute(val->collection, "media-left-margin",
IPP_TAG_INTEGER)) != NULL)
mdb.left = media_attr->values[0].integer;
if ((media_attr = ippFindAttribute(val->collection, "media-right-margin",
IPP_TAG_INTEGER)) != NULL)
mdb.right = media_attr->values[0].integer;
if ((media_attr = ippFindAttribute(val->collection, "media-top-margin",
IPP_TAG_INTEGER)) != NULL)
mdb.top = media_attr->values[0].integer;
cupsArrayAdd(db, &mdb);
}
if (custom)
{
if ((media_attr = ippFindAttribute(custom->collection,
"media-bottom-margin",
IPP_TAG_INTEGER)) != NULL)
{
dinfo->min_size.top =
dinfo->max_size.top = media_attr->values[0].integer;
}
if ((media_attr = ippFindAttribute(custom->collection,
"media-left-margin",
IPP_TAG_INTEGER)) != NULL)
{
dinfo->min_size.left =
dinfo->max_size.left = media_attr->values[0].integer;
}
if ((media_attr = ippFindAttribute(custom->collection,
"media-right-margin",
IPP_TAG_INTEGER)) != NULL)
{
dinfo->min_size.right =
dinfo->max_size.right = media_attr->values[0].integer;
}
if ((media_attr = ippFindAttribute(custom->collection,
"media-top-margin",
IPP_TAG_INTEGER)) != NULL)
{
dinfo->min_size.top =
dinfo->max_size.top = media_attr->values[0].integer;
}
}
}
else if (media_attr &&
(media_attr->value_tag == IPP_TAG_NAME ||
media_attr->value_tag == IPP_TAG_NAMELANG ||
media_attr->value_tag == IPP_TAG_KEYWORD))
{
memset(&mdb, 0, sizeof(mdb));
mdb.left =
mdb.right = 635;
mdb.top =
mdb.bottom = 1270;
for (i = media_attr->num_values, val = media_attr->values;
i > 0;
i --, val ++)
{
if ((pwg = pwgMediaForPWG(val->string.text)) == NULL)
if ((pwg = pwgMediaForLegacy(val->string.text)) == NULL)
{
DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.",
val->string.text));
continue;
}
mdb.width = pwg->width;
mdb.length = pwg->length;
if (flags != CUPS_MEDIA_FLAGS_READY &&
!strncmp(val->string.text, "custom_min_", 11))
{
mdb.size_name = NULL;
dinfo->min_size = mdb;
}
else if (flags != CUPS_MEDIA_FLAGS_READY &&
!strncmp(val->string.text, "custom_max_", 11))
{
mdb.size_name = NULL;
dinfo->max_size = mdb;
}
else
{
mdb.size_name = val->string.text;
cupsArrayAdd(db, &mdb);
}
}
}
}
static void
cups_free_media_db(
_cups_media_db_t *mdb)
{
if (mdb->color)
_cupsStrFree(mdb->color);
if (mdb->key)
_cupsStrFree(mdb->key);
if (mdb->info)
_cupsStrFree(mdb->info);
if (mdb->size_name)
_cupsStrFree(mdb->size_name);
if (mdb->source)
_cupsStrFree(mdb->source);
if (mdb->type)
_cupsStrFree(mdb->type);
free(mdb);
}
static int
cups_get_media_db(http_t *http,
cups_dinfo_t *dinfo,
pwg_media_t *pwg,
unsigned flags,
cups_size_t *size)
{
cups_array_t *db;
_cups_media_db_t *mdb,
*best = NULL,
key;
if (flags & CUPS_MEDIA_FLAGS_READY)
{
cups_update_ready(http, dinfo);
db = dinfo->ready_db;
}
else
{
if (!dinfo->media_db)
cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_DEFAULT);
db = dinfo->media_db;
}
memset(&key, 0, sizeof(key));
key.width = pwg->width;
key.length = pwg->length;
if ((mdb = cupsArrayFind(db, &key)) != NULL)
{
best = mdb;
if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
{
if (best->left != 0 || best->right != 0 || best->top != 0 ||
best->bottom != 0)
{
for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
mdb && !cups_compare_media_db(mdb, &key);
mdb = (_cups_media_db_t *)cupsArrayNext(db))
{
if (mdb->left <= best->left && mdb->right <= best->right &&
mdb->top <= best->top && mdb->bottom <= best->bottom)
{
best = mdb;
if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
mdb->top == 0)
break;
}
}
}
if ((flags & CUPS_MEDIA_FLAGS_EXACT) &&
(best->left || best->right || best->top || best->bottom))
return (0);
}
else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
{
for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
mdb && !cups_compare_media_db(mdb, &key);
mdb = (_cups_media_db_t *)cupsArrayNext(db))
{
if (mdb->left >= best->left && mdb->right >= best->right &&
mdb->top >= best->top && mdb->bottom >= best->bottom)
best = mdb;
}
}
else
{
for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
mdb && !cups_compare_media_db(mdb, &key);
mdb = (_cups_media_db_t *)cupsArrayNext(db))
{
if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
((mdb->right > 0 && mdb->right <= best->right) ||
best->right == 0) &&
((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
((mdb->bottom > 0 && mdb->bottom <= best->bottom) ||
best->bottom == 0))
best = mdb;
}
}
}
else if (flags & CUPS_MEDIA_FLAGS_EXACT)
{
if (pwg->width < dinfo->min_size.width ||
pwg->width > dinfo->max_size.width ||
pwg->length < dinfo->min_size.length ||
pwg->length > dinfo->max_size.length)
return (0);
if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) &&
(dinfo->min_size.left > 0 || dinfo->min_size.right > 0 ||
dinfo->min_size.top > 0 || dinfo->min_size.bottom > 0))
return (0);
key.size_name = (char *)pwg->pwg;
key.bottom = dinfo->min_size.bottom;
key.left = dinfo->min_size.left;
key.right = dinfo->min_size.right;
key.top = dinfo->min_size.top;
best = &key;
}
else if (pwg->width >= dinfo->min_size.width &&
pwg->width <= dinfo->max_size.width &&
pwg->length >= dinfo->min_size.length &&
pwg->length <= dinfo->max_size.length)
{
key.size_name = (char *)pwg->pwg;
key.bottom = dinfo->min_size.bottom;
key.left = dinfo->min_size.left;
key.right = dinfo->min_size.right;
key.top = dinfo->min_size.top;
best = &key;
}
else
{
for (mdb = (_cups_media_db_t *)cupsArrayFirst(db);
mdb;
mdb = (_cups_media_db_t *)cupsArrayNext(db))
if (cups_is_close_media_db(mdb, &key))
break;
if (!mdb)
return (0);
best = mdb;
if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
{
if (best->left != 0 || best->right != 0 || best->top != 0 ||
best->bottom != 0)
{
for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
mdb && cups_is_close_media_db(mdb, &key);
mdb = (_cups_media_db_t *)cupsArrayNext(db))
{
if (mdb->left <= best->left && mdb->right <= best->right &&
mdb->top <= best->top && mdb->bottom <= best->bottom)
{
best = mdb;
if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
mdb->top == 0)
break;
}
}
}
}
else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
{
for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
mdb && cups_is_close_media_db(mdb, &key);
mdb = (_cups_media_db_t *)cupsArrayNext(db))
{
if (mdb->left >= best->left && mdb->right >= best->right &&
mdb->top >= best->top && mdb->bottom >= best->bottom)
best = mdb;
}
}
else
{
for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
mdb && cups_is_close_media_db(mdb, &key);
mdb = (_cups_media_db_t *)cupsArrayNext(db))
{
if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
((mdb->right > 0 && mdb->right <= best->right) ||
best->right == 0) &&
((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
((mdb->bottom > 0 && mdb->bottom <= best->bottom) ||
best->bottom == 0))
best = mdb;
}
}
}
if (best)
{
if (best->size_name)
strlcpy(size->media, best->size_name, sizeof(size->media));
else if (best->key)
strlcpy(size->media, best->key, sizeof(size->media));
else
strlcpy(size->media, pwg->pwg, sizeof(size->media));
size->width = best->width;
size->length = best->length;
size->bottom = best->bottom;
size->left = best->left;
size->right = best->right;
size->top = best->top;
return (1);
}
return (0);
}
static int
cups_is_close_media_db(
_cups_media_db_t *a,
_cups_media_db_t *b)
{
int dwidth,
dlength;
dwidth = a->width - b->width;
dlength = a->length - b->length;
return (dwidth >= -176 && dwidth <= 176 &&
dlength >= -176 && dlength <= 176);
}
static cups_array_t *
cups_test_constraints(
cups_dinfo_t *dinfo,
const char *new_option,
const char *new_value,
int num_options,
cups_option_t *options,
int *num_conflicts,
cups_option_t **conflicts)
{
int i,
match;
int num_matching;
cups_option_t *matching;
_cups_dconstres_t *c;
cups_array_t *active = NULL;
ipp_attribute_t *attr;
_ipp_value_t *attrval;
const char *value;
char temp[1024];
int int_value;
int xres_value,
yres_value;
ipp_res_t units_value;
for (c = (_cups_dconstres_t *)cupsArrayFirst(dinfo->constraints);
c;
c = (_cups_dconstres_t *)cupsArrayNext(dinfo->constraints))
{
num_matching = 0;
matching = NULL;
for (attr = ippFirstAttribute(c->collection);
attr;
attr = ippNextAttribute(c->collection))
{
if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
break;
if (new_option && new_value && !strcmp(attr->name, new_option))
value = new_value;
else if ((value = cupsGetOption(attr->name, num_options,
options)) == NULL)
value = cupsGetOption(attr->name, dinfo->num_defaults, dinfo->defaults);
if (!value)
{
break;
}
match = 0;
switch (attr->value_tag)
{
case IPP_TAG_INTEGER :
case IPP_TAG_ENUM :
int_value = atoi(value);
for (i = attr->num_values, attrval = attr->values;
i > 0;
i --, attrval ++)
{
if (attrval->integer == int_value)
{
match = 1;
break;
}
}
break;
case IPP_TAG_BOOLEAN :
int_value = !strcmp(value, "true");
for (i = attr->num_values, attrval = attr->values;
i > 0;
i --, attrval ++)
{
if (attrval->boolean == int_value)
{
match = 1;
break;
}
}
break;
case IPP_TAG_RANGE :
int_value = atoi(value);
for (i = attr->num_values, attrval = attr->values;
i > 0;
i --, attrval ++)
{
if (int_value >= attrval->range.lower &&
int_value <= attrval->range.upper)
{
match = 1;
break;
}
}
break;
case IPP_TAG_RESOLUTION :
if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
{
if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
break;
yres_value = xres_value;
}
if (!strcmp(temp, "dpi"))
units_value = IPP_RES_PER_INCH;
else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm"))
units_value = IPP_RES_PER_CM;
else
break;
for (i = attr->num_values, attrval = attr->values;
i > 0;
i --, attrval ++)
{
if (attrval->resolution.xres == xres_value &&
attrval->resolution.yres == yres_value &&
attrval->resolution.units == units_value)
{
match = 1;
break;
}
}
break;
case IPP_TAG_TEXT :
case IPP_TAG_NAME :
case IPP_TAG_KEYWORD :
case IPP_TAG_CHARSET :
case IPP_TAG_URI :
case IPP_TAG_URISCHEME :
case IPP_TAG_MIMETYPE :
case IPP_TAG_LANGUAGE :
case IPP_TAG_TEXTLANG :
case IPP_TAG_NAMELANG :
for (i = attr->num_values, attrval = attr->values;
i > 0;
i --, attrval ++)
{
if (!strcmp(attrval->string.text, value))
{
match = 1;
break;
}
}
break;
default :
break;
}
if (!match)
break;
num_matching = cupsAddOption(attr->name, value, num_matching, &matching);
}
if (!attr)
{
if (!active)
active = cupsArrayNew(NULL, NULL);
cupsArrayAdd(active, c);
if (num_conflicts && conflicts)
{
cups_option_t *moption;
for (i = num_matching, moption = matching; i > 0; i --, moption ++)
*num_conflicts = cupsAddOption(moption->name, moption->value,
*num_conflicts, conflicts);
}
}
cupsFreeOptions(num_matching, matching);
}
return (active);
}
static void
cups_update_ready(http_t *http,
cups_dinfo_t *dinfo)
{
ipp_t *request;
static const char * const pattrs[] =
{
"finishings-col-ready",
"finishings-ready",
"job-finishings-col-ready",
"job-finishings-ready",
"media-col-ready",
"media-ready"
};
if ((time(NULL) - dinfo->ready_time) < _CUPS_MEDIA_READY_TTL)
return;
if (dinfo->cached_flags & CUPS_MEDIA_FLAGS_READY)
{
cupsArrayDelete(dinfo->cached_db);
dinfo->cached_db = NULL;
dinfo->cached_flags = CUPS_MEDIA_FLAGS_DEFAULT;
}
ippDelete(dinfo->ready_attrs);
dinfo->ready_attrs = NULL;
cupsArrayDelete(dinfo->ready_db);
dinfo->ready_db = NULL;
request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
ippSetVersion(request, dinfo->version / 10, dinfo->version % 10);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
dinfo->uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
NULL, cupsUser());
ippAddStrings(request, IPP_TAG_OPERATION,
IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, "requested-attributes",
(int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
dinfo->ready_attrs = cupsDoRequest(http, request, dinfo->resource);
cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_READY);
dinfo->ready_time = time(NULL);
}