+ if ((*attr)->num_values > 0)
+ ipp_free_values(*attr, 0, (*attr)->num_values);
+
+ /*
+ * Set out-of-band value...
+ */
+
+ (*attr)->value_tag = value_tag;
+ break;
+
+ case IPP_TAG_RANGE :
+ if (temp_tag != IPP_TAG_INTEGER)
+ return (0);
+
+ for (i = (*attr)->num_values, value = (*attr)->values;
+ i > 0;
+ i --, value ++)
+ {
+ integer = value->integer;
+ value->range.lower = value->range.upper = integer;
+ }
+
+ (*attr)->value_tag = IPP_TAG_RANGE;
+ break;
+
+ case IPP_TAG_NAME :
+ if (temp_tag != IPP_TAG_KEYWORD && temp_tag != IPP_TAG_URI &&
+ temp_tag != IPP_TAG_URISCHEME && temp_tag != IPP_TAG_LANGUAGE &&
+ temp_tag != IPP_TAG_MIMETYPE)
+ return (0);
+
+ (*attr)->value_tag = (ipp_tag_t)(IPP_TAG_NAME | ((*attr)->value_tag & IPP_TAG_CUPS_CONST));
+ break;
+
+ case IPP_TAG_NAMELANG :
+ case IPP_TAG_TEXTLANG :
+ if (value_tag == IPP_TAG_NAMELANG &&
+ (temp_tag != IPP_TAG_NAME && temp_tag != IPP_TAG_KEYWORD &&
+ temp_tag != IPP_TAG_URI && temp_tag != IPP_TAG_URISCHEME &&
+ temp_tag != IPP_TAG_LANGUAGE && temp_tag != IPP_TAG_MIMETYPE))
+ return (0);
+
+ if (value_tag == IPP_TAG_TEXTLANG && temp_tag != IPP_TAG_TEXT)
+ return (0);
+
+ if (ipp->attrs && ipp->attrs->next && ipp->attrs->next->name &&
+ !strcmp(ipp->attrs->next->name, "attributes-natural-language"))
+ {
+ /*
+ * Use the language code from the IPP message...
+ */
+
+ (*attr)->values[0].string.language =
+ _cupsStrAlloc(ipp->attrs->next->values[0].string.text);
+ }
+ else
+ {
+ /*
+ * Otherwise, use the language code corresponding to the locale...
+ */
+
+ language = cupsLangDefault();
+ (*attr)->values[0].string.language = _cupsStrAlloc(ipp_lang_code(language->language,
+ code,
+ sizeof(code)));
+ }
+
+ for (i = (*attr)->num_values - 1, value = (*attr)->values + 1;
+ i > 0;
+ i --, value ++)
+ value->string.language = (*attr)->values[0].string.language;
+
+ if ((int)(*attr)->value_tag & IPP_TAG_CUPS_CONST)
+ {
+ /*
+ * Make copies of all values...
+ */
+
+ for (i = (*attr)->num_values, value = (*attr)->values;
+ i > 0;
+ i --, value ++)
+ value->string.text = _cupsStrAlloc(value->string.text);
+ }
+
+ (*attr)->value_tag = IPP_TAG_NAMELANG;
+ break;
+
+ case IPP_TAG_KEYWORD :
+ if (temp_tag == IPP_TAG_NAME || temp_tag == IPP_TAG_NAMELANG)
+ break; /* Silently "allow" name -> keyword */
+
+ default :
+ return (0);
+ }
+
+ return (1);
+}
+
+
+/*
+ * 'ippSetVersion()' - Set the version number in an IPP message.
+ *
+ * The @code ipp@ parameter refers to an IPP message previously created using
+ * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
+ *
+ * The valid version numbers are currently 1.0, 1.1, 2.0, 2.1, and 2.2.
+ *
+ * @since CUPS 1.6/macOS 10.8@
+ */
+
+int /* O - 1 on success, 0 on failure */
+ippSetVersion(ipp_t *ipp, /* I - IPP message */
+ int major, /* I - Major version number (major.minor) */
+ int minor) /* I - Minor version number (major.minor) */
+{
+ /*
+ * Range check input...
+ */
+
+ if (!ipp || major < 0 || minor < 0)
+ return (0);
+
+ /*
+ * Set the version number...
+ */
+
+ ipp->request.any.version[0] = (ipp_uchar_t)major;
+ ipp->request.any.version[1] = (ipp_uchar_t)minor;
+
+ return (1);
+}
+
+
+/*
+ * 'ippTimeToDate()' - Convert from time in seconds to RFC 2579 format.
+ */
+
+const ipp_uchar_t * /* O - RFC-2579 date/time data */
+ippTimeToDate(time_t t) /* I - Time in seconds */
+{
+ struct tm *unixdate; /* UNIX unixdate/time info */
+ ipp_uchar_t *date = _cupsGlobals()->ipp_date;
+ /* RFC-2579 date/time data */
+
+
+ /*
+ * RFC-2579 date/time format is:
+ *
+ * Byte(s) Description
+ * ------- -----------
+ * 0-1 Year (0 to 65535)
+ * 2 Month (1 to 12)
+ * 3 Day (1 to 31)
+ * 4 Hours (0 to 23)
+ * 5 Minutes (0 to 59)
+ * 6 Seconds (0 to 60, 60 = "leap second")
+ * 7 Deciseconds (0 to 9)
+ * 8 +/- UTC
+ * 9 UTC hours (0 to 11)
+ * 10 UTC minutes (0 to 59)
+ */
+
+ unixdate = gmtime(&t);
+ unixdate->tm_year += 1900;
+
+ date[0] = (ipp_uchar_t)(unixdate->tm_year >> 8);
+ date[1] = (ipp_uchar_t)(unixdate->tm_year);
+ date[2] = (ipp_uchar_t)(unixdate->tm_mon + 1);
+ date[3] = (ipp_uchar_t)unixdate->tm_mday;
+ date[4] = (ipp_uchar_t)unixdate->tm_hour;
+ date[5] = (ipp_uchar_t)unixdate->tm_min;
+ date[6] = (ipp_uchar_t)unixdate->tm_sec;
+ date[7] = 0;
+ date[8] = '+';
+ date[9] = 0;
+ date[10] = 0;
+
+ return (date);
+}
+
+
+/*
+ * 'ippValidateAttribute()' - Validate the contents of an attribute.
+ *
+ * This function validates the contents of an attribute based on the name and
+ * value tag. 1 is returned if the attribute is valid, 0 otherwise. On
+ * failure, @link cupsLastErrorString@ is set to a human-readable message.
+ *
+ * @since CUPS 1.7/macOS 10.9@
+ */
+
+int /* O - 1 if valid, 0 otherwise */
+ippValidateAttribute(
+ ipp_attribute_t *attr) /* I - Attribute */
+{
+ int i; /* Looping var */
+ char scheme[64], /* Scheme from URI */
+ userpass[256], /* Username/password from URI */
+ hostname[256], /* Hostname from URI */
+ resource[1024]; /* Resource from URI */
+ int port, /* Port number from URI */
+ uri_status; /* URI separation status */
+ const char *ptr; /* Pointer into string */
+ ipp_attribute_t *colattr; /* Collection attribute */
+ regex_t re; /* Regular expression */
+ ipp_uchar_t *date; /* Current date value */
+
+
+ /*
+ * Skip separators.
+ */
+
+ if (!attr->name)
+ return (1);
+
+ /*
+ * Validate the attribute name.
+ */
+
+ for (ptr = attr->name; *ptr; ptr ++)
+ if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_')
+ break;
+
+ if (*ptr || ptr == attr->name)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad attribute name - invalid character (RFC 8011 section 5.1.4)."), attr->name);
+ return (0);
+ }
+
+ if ((ptr - attr->name) > 255)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad attribute name - bad length %d (RFC 8011 section 5.1.4)."), attr->name, (int)(ptr - attr->name));
+ return (0);
+ }
+
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (attr->values[i].boolean != 0 &&
+ attr->values[i].boolean != 1)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad boolean value %d (RFC 8011 section 5.1.21)."), attr->name, attr->values[i].boolean);
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_ENUM :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (attr->values[i].integer < 1)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad enum value %d - out of range (RFC 8011 section 5.1.5)."), attr->name, attr->values[i].integer);
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_STRING :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (attr->values[i].unknown.length > IPP_MAX_OCTETSTRING)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad octetString value - bad length %d (RFC 8011 section 5.1.20)."), attr->name, attr->values[i].unknown.length);
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_DATE :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ date = attr->values[i].date;
+
+ if (date[2] < 1 || date[2] > 12)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime month %u (RFC 8011 section 5.1.15)."), attr->name, date[2]);
+ return (0);
+ }
+
+ if (date[3] < 1 || date[3] > 31)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime day %u (RFC 8011 section 5.1.15)."), attr->name, date[3]);
+ return (0);
+ }
+
+ if (date[4] > 23)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime hours %u (RFC 8011 section 5.1.15)."), attr->name, date[4]);
+ return (0);
+ }
+
+ if (date[5] > 59)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime minutes %u (RFC 8011 section 5.1.15)."), attr->name, date[5]);
+ return (0);
+ }
+
+ if (date[6] > 60)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime seconds %u (RFC 8011 section 5.1.15)."), attr->name, date[6]);
+ return (0);
+ }
+
+ if (date[7] > 9)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime deciseconds %u (RFC 8011 section 5.1.15)."), attr->name, date[7]);
+ return (0);
+ }
+
+ if (date[8] != '-' && date[8] != '+')
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime UTC sign '%c' (RFC 8011 section 5.1.15)."), attr->name, date[8]);
+ return (0);
+ }
+
+ if (date[9] > 11)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime UTC hours %u (RFC 8011 section 5.1.15)."), attr->name, date[9]);
+ return (0);
+ }
+
+ if (date[10] > 59)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime UTC minutes %u (RFC 8011 section 5.1.15)."), attr->name, date[10]);
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (attr->values[i].resolution.xres <= 0)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad resolution value %dx%d%s - cross feed resolution must be positive (RFC 8011 section 5.1.16)."), attr->name, attr->values[i].resolution.xres, attr->values[i].resolution.yres, attr->values[i].resolution.units == IPP_RES_PER_INCH ? "dpi" : attr->values[i].resolution.units == IPP_RES_PER_CM ? "dpcm" : "unknown");
+ return (0);
+ }
+
+ if (attr->values[i].resolution.yres <= 0)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad resolution value %dx%d%s - feed resolution must be positive (RFC 8011 section 5.1.16)."), attr->name, attr->values[i].resolution.xres, attr->values[i].resolution.yres, attr->values[i].resolution.units == IPP_RES_PER_INCH ? "dpi" : attr->values[i].resolution.units == IPP_RES_PER_CM ? "dpcm" : "unknown");
+ return (0);
+ }
+
+ if (attr->values[i].resolution.units != IPP_RES_PER_INCH && attr->values[i].resolution.units != IPP_RES_PER_CM)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad resolution value %dx%d%s - bad units value (RFC 8011 section 5.1.16)."), attr->name, attr->values[i].resolution.xres, attr->values[i].resolution.yres, attr->values[i].resolution.units == IPP_RES_PER_INCH ? "dpi" : attr->values[i].resolution.units == IPP_RES_PER_CM ? "dpcm" : "unknown");
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_RANGE :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (attr->values[i].range.lower > attr->values[i].range.upper)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad rangeOfInteger value %d-%d - lower greater than upper (RFC 8011 section 5.1.14)."), attr->name, attr->values[i].range.lower, attr->values[i].range.upper);
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_BEGIN_COLLECTION :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ for (colattr = attr->values[i].collection->attrs;
+ colattr;
+ colattr = colattr->next)
+ {
+ if (!ippValidateAttribute(colattr))
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_TEXT :
+ case IPP_TAG_TEXTLANG :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ for (ptr = attr->values[i].string.text; *ptr; ptr ++)
+ {
+ if ((*ptr & 0xe0) == 0xc0)
+ {
+ if ((ptr[1] & 0xc0) != 0x80)
+ break;
+
+ ptr ++;
+ }
+ else if ((*ptr & 0xf0) == 0xe0)
+ {
+ if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80)
+ break;
+
+ ptr += 2;
+ }
+ else if ((*ptr & 0xf8) == 0xf0)
+ {
+ if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80 || (ptr[3] & 0xc0) != 0x80)
+ break;
+
+ ptr += 3;
+ }
+ else if (*ptr & 0x80)
+ break;
+ else if ((*ptr < ' ' && *ptr != '\n' && *ptr != '\r' && *ptr != '\t') || *ptr == 0x7f)
+ break;
+ }
+
+ if (*ptr)
+ {
+ if (*ptr < ' ' || *ptr == 0x7f)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad text value \"%s\" - bad control character (PWG 5100.14 section 8.3)."), attr->name, attr->values[i].string.text);
+ return (0);
+ }
+ else
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad text value \"%s\" - bad UTF-8 sequence (RFC 8011 section 5.1.2)."), attr->name, attr->values[i].string.text);
+ return (0);
+ }
+ }
+
+ if ((ptr - attr->values[i].string.text) > (IPP_MAX_TEXT - 1))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad text value \"%s\" - bad length %d (RFC 8011 section 5.1.2)."), attr->name, attr->values[i].string.text, (int)(ptr - attr->values[i].string.text));
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_NAME :
+ case IPP_TAG_NAMELANG :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ for (ptr = attr->values[i].string.text; *ptr; ptr ++)
+ {
+ if ((*ptr & 0xe0) == 0xc0)
+ {
+ if ((ptr[1] & 0xc0) != 0x80)
+ break;
+
+ ptr ++;
+ }
+ else if ((*ptr & 0xf0) == 0xe0)
+ {
+ if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80)
+ break;
+
+ ptr += 2;
+ }
+ else if ((*ptr & 0xf8) == 0xf0)
+ {
+ if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80 || (ptr[3] & 0xc0) != 0x80)
+ break;
+
+ ptr += 3;
+ }
+ else if (*ptr & 0x80)
+ break;
+ else if (*ptr < ' ' || *ptr == 0x7f)
+ break;
+ }
+
+ if (*ptr)
+ {
+ if (*ptr < ' ' || *ptr == 0x7f)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad name value \"%s\" - bad control character (PWG 5100.14 section 8.1)."), attr->name, attr->values[i].string.text);
+ return (0);
+ }
+ else
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad name value \"%s\" - bad UTF-8 sequence (RFC 8011 section 5.1.3)."), attr->name, attr->values[i].string.text);
+ return (0);
+ }
+ }
+
+ if ((ptr - attr->values[i].string.text) > (IPP_MAX_NAME - 1))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad name value \"%s\" - bad length %d (RFC 8011 section 5.1.3)."), attr->name, attr->values[i].string.text, (int)(ptr - attr->values[i].string.text));
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_KEYWORD :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ for (ptr = attr->values[i].string.text; *ptr; ptr ++)
+ {
+ if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' &&
+ *ptr != '_')
+ break;
+ }
+
+ if (*ptr || ptr == attr->values[i].string.text)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad keyword value \"%s\" - invalid character (RFC 8011 section 5.1.4)."), attr->name, attr->values[i].string.text);
+ return (0);
+ }
+
+ if ((ptr - attr->values[i].string.text) > (IPP_MAX_KEYWORD - 1))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad keyword value \"%s\" - bad length %d (RFC 8011 section 5.1.4)."), attr->name, attr->values[i].string.text, (int)(ptr - attr->values[i].string.text));
+ return (0);
+ }
+ }
+ break;
+
+ case IPP_TAG_URI :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource));
+
+ if (uri_status < HTTP_URI_STATUS_OK)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad URI value \"%s\" - %s (RFC 8011 section 5.1.6)."), attr->name, attr->values[i].string.text, httpURIStatusString(uri_status));
+ return (0);
+ }
+
+ if (strlen(attr->values[i].string.text) > (IPP_MAX_URI - 1))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad URI value \"%s\" - bad length %d (RFC 8011 section 5.1.6)."), attr->name, attr->values[i].string.text, (int)strlen(attr->values[i].string.text));
+ }
+ }
+ break;
+
+ case IPP_TAG_URISCHEME :
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ ptr = attr->values[i].string.text;
+ if (islower(*ptr & 255))
+ {
+ for (ptr ++; *ptr; ptr ++)
+ {
+ if (!islower(*ptr & 255) && !isdigit(*ptr & 255) &&
+ *ptr != '+' && *ptr != '-' && *ptr != '.')
+ break;
+ }
+ }
+
+ if (*ptr || ptr == attr->values[i].string.text)
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad uriScheme value \"%s\" - bad characters (RFC 8011 section 5.1.7)."), attr->name, attr->values[i].string.text);
+ return (0);
+ }
+
+ if ((ptr - attr->values[i].string.text) > (IPP_MAX_URISCHEME - 1))
+ {
+ ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad uriScheme value \"%s\" - bad length %d (RFC 8011 section 5.1.7)."), attr->name, attr->values[i].string.text, (int)(ptr - attr->values[i].string.text));
+ return (0);
+ }
+ }
+ break;