diff --git a/CHANGES.md b/CHANGES.md index 197b317a0..7787acc35 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -113,6 +113,7 @@ Changes in CUPS v2.5b1 (TBA) extension. - Fixed error handling when reading a mixed `1setOf` attribute. - Fixed how `ippeveprinter` responds to an unsupported request character set. +- Fixed a recursion issue in `ippReadIO`. - Removed hash support for SHA2-512-224 and SHA2-512-256. - Removed `mantohtml` script for generating html pages (use `https://www.msweet.org/mantohtml/`) diff --git a/cups/ipp.c b/cups/ipp.c index fdfd21b1c..7838e80e9 100644 --- a/cups/ipp.c +++ b/cups/ipp.c @@ -25,8 +25,9 @@ static void ipp_free_values(ipp_attribute_t *attr, int element, int count); static char *ipp_get_code(const char *locale, char *buffer, size_t bufsize) _CUPS_NONNULL(1,2); static char *ipp_lang_code(const char *locale, char *buffer, size_t bufsize) _CUPS_NONNULL(1,2); static size_t ipp_length(ipp_t *ipp, int collection); -static ssize_t ipp_read_http(http_t *http, ipp_uchar_t *buffer, size_t length); static ssize_t ipp_read_file(int *fd, ipp_uchar_t *buffer, size_t length); +static ssize_t ipp_read_http(http_t *http, ipp_uchar_t *buffer, size_t length); +static ipp_state_t ipp_read_io(void *src, ipp_io_cb_t cb, int blocking, ipp_t *parent, ipp_t *ipp, int depth); static void ipp_set_error(ipp_status_t status, const char *format, ...); static _ipp_value_t *ipp_set_value(ipp_t *ipp, ipp_attribute_t **attr, int element); static ssize_t ipp_write_file(int *fd, ipp_uchar_t *buffer, size_t length); @@ -2588,12 +2589,12 @@ ippRead(http_t *http, // I - HTTP connection { DEBUG_printf("ippRead(http=%p, ipp=%p), data_remaining=" CUPS_LLFMT, (void *)http, (void *)ipp, CUPS_LLCAST (http ? http->data_remaining : -1)); - if (!http) + if (!http || !ipp) return (IPP_STATE_ERROR); DEBUG_printf("2ippRead: http->state=%d, http->used=%d", http->state, http->used); - return (ippReadIO(http, (ipp_io_cb_t)ipp_read_http, http->blocking, NULL, ipp)); + return (ipp_read_io(http, (ipp_io_cb_t)ipp_read_http, http->blocking, /*parent*/NULL, ipp, /*depth*/0)); } @@ -2609,7 +2610,10 @@ ippReadFile(int fd, // I - HTTP data { DEBUG_printf("ippReadFile(fd=%d, ipp=%p)", fd, (void *)ipp); - return (ippReadIO(&fd, (ipp_io_cb_t)ipp_read_file, 1, NULL, ipp)); + if (!ipp) + return (IPP_STATE_ERROR); + + return (ipp_read_io(&fd, (ipp_io_cb_t)ipp_read_file, /*blocking*/1, /*parent*/NULL, ipp, /*depth*/0)); } @@ -2626,649 +2630,445 @@ ippReadIO(void *src, // I - Data source ipp_t *parent, // I - Parent request, if any ipp_t *ipp) // I - IPP data { - int n; // Length of data - unsigned char *buffer, // Data buffer - string[IPP_MAX_TEXT], - // Small string buffer - *bufptr, // Pointer into buffer - *bufend; // End of buffer - ipp_attribute_t *attr = NULL; // Current attribute - ipp_tag_t tag; // Current tag - ipp_tag_t value_tag; // Current value tag - _ipp_value_t *value; // Current value - - DEBUG_printf("ippReadIO(src=%p, cb=%p, blocking=%d, parent=%p, ipp=%p)", (void *)src, (void *)cb, blocking, (void *)parent, (void *)ipp); - DEBUG_printf("2ippReadIO: ipp->state=%d", ipp ? ipp->state : IPP_STATE_ERROR); if (!src || !ipp) return (IPP_STATE_ERROR); - if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL) + return (ipp_read_io(src, cb, blocking, parent, ipp, /*depth*/0)); +} + + +// +// 'ippRestore()' - Restore a previously saved find position. +// +// @since CUPS 2.5@ +// + +void +ippRestore(ipp_t *ipp) // I - IPP message +{ + if (ipp) { - DEBUG_puts("1ippReadIO: Unable to get read buffer."); - return (IPP_STATE_ERROR); + if (ipp->find > ipp->fstack) + ipp->find --; + else + ipp->find = NULL; } +} - switch (ipp->state) + +// +// 'ippSave()' - Save the current find position. +// +// @since CUPS 2.5@ +// + +void +ippSave(ipp_t *ipp) // I - IPP message +{ + if (ipp && ipp->find < (ipp->fstack + _IPP_MAX_FIND)) { - case IPP_STATE_IDLE : - ipp->state ++; // Avoid common problem... + ipp->find[1] = ipp->find[0]; + ipp->find ++; + } +} - case IPP_STATE_HEADER : - if (parent == NULL) - { - // Get the request header... - if ((*cb)(src, buffer, 8) < 8) - { - DEBUG_puts("1ippReadIO: Unable to read header."); - goto rollback; - } - // Then copy the request header over... - ipp->request.any.version[0] = buffer[0]; - ipp->request.any.version[1] = buffer[1]; - ipp->request.any.op_status = (buffer[2] << 8) | buffer[3]; - ipp->request.any.request_id = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; +// +// 'ippSetBoolean()' - Set a boolean value in an attribute. +// +// The "ipp" parameter refers to an IPP message previously created using +// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. +// +// The "attr" parameter may be modified as a result of setting the value. +// +// The "element" parameter specifies which value to set from 0 to +// @code ippGetCount(attr)@. +// +// @since CUPS 1.6/macOS 10.8@ +// - DEBUG_printf("2ippReadIO: version=%d.%d", buffer[0], buffer[1]); - DEBUG_printf("2ippReadIO: op_status=%04x", ipp->request.any.op_status); - DEBUG_printf("2ippReadIO: request_id=%d", ipp->request.any.request_id); - } +int // O - 1 on success, 0 on failure +ippSetBoolean(ipp_t *ipp, // I - IPP message + ipp_attribute_t **attr, // IO - IPP attribute + int element, // I - Value number (0-based) + int boolvalue)// I - Boolean value +{ + _ipp_value_t *value; // Current value - ipp->state = IPP_STATE_ATTRIBUTE; - ipp->current = NULL; - ipp->curtag = IPP_TAG_ZERO; - ipp->prev = ipp->last; - // If blocking is disabled, stop here... - if (!blocking) - break; + // Range check input... + if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BOOLEAN || element < 0 || element > (*attr)->num_values) + return (0); - case IPP_STATE_ATTRIBUTE : - for (;;) - { - if ((*cb)(src, buffer, 1) < 1) - { - DEBUG_puts("1ippReadIO: Callback returned EOF/error"); - goto rollback; - } + // Set the value and return... + if ((value = ipp_set_value(ipp, attr, element)) != NULL) + value->boolean = (char)boolvalue; - DEBUG_printf("2ippReadIO: ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev); + return (value != NULL); +} - // Read this attribute... - tag = (ipp_tag_t)buffer[0]; - if (tag == IPP_TAG_END) - { - // No more attributes left... - DEBUG_puts("2ippReadIO: IPP_TAG_END."); +// +// 'ippSetCollection()' - Set a collection value in an attribute. +// +// The "ipp" parameter refers to an IPP message previously created using +// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. +// +// The "attr" parameter may be modified as a result of setting the value. +// +// The "element" parameter specifies which value to set from 0 to +// @code ippGetCount(attr)@. +// +// @since CUPS 1.6/macOS 10.8@ +// - ipp->state = IPP_STATE_DATA; - break; - } - else if (tag == IPP_TAG_ZERO || (tag == IPP_TAG_OPERATION && ipp->curtag != IPP_TAG_ZERO)) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid group tag."), 1); - DEBUG_printf("1ippReadIO: bad tag 0x%02x.", tag); - goto rollback; - } - else if (tag < IPP_TAG_UNSUPPORTED_VALUE) - { - // Group tag... Set the current group and continue... - if (parent) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid group tag."), 1); - DEBUG_printf("1ippReadIO: bad tag 0x%02x.", tag); - goto rollback; - } - else if (ipp->curtag == tag) - { - ipp->prev = ippAddSeparator(ipp); - } - else if (ipp->current) - { - ipp->prev = ipp->current; - } +int // O - 1 on success, 0 on failure +ippSetCollection( + ipp_t *ipp, // I - IPP message + ipp_attribute_t **attr, // IO - IPP attribute + int element, // I - Value number (0-based) + ipp_t *colvalue) // I - Collection value +{ + _ipp_value_t *value; // Current value - ipp->curtag = tag; - ipp->current = NULL; - attr = NULL; - DEBUG_printf("2ippReadIO: group tag=%x(%s), ipp->prev=%p", tag, ippTagString(tag), (void *)ipp->prev); - continue; - } - DEBUG_printf("2ippReadIO: value tag=%x(%s)", tag, ippTagString(tag)); + // Range check input... + if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BEGIN_COLLECTION || element < 0 || element > (*attr)->num_values || !colvalue) + return (0); - // Get the name... - if ((*cb)(src, buffer, 2) < 2) - { - DEBUG_puts("1ippReadIO: unable to read name length."); - goto rollback; - } + // Set the value and return... + if ((value = ipp_set_value(ipp, attr, element)) != NULL) + { + if (value->collection) + ippDelete(value->collection); - n = (buffer[0] << 8) | buffer[1]; + value->collection = colvalue; + colvalue->use ++; + } - if (n >= IPP_BUF_SIZE) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP name larger than 32767 bytes."), 1); - DEBUG_printf("1ippReadIO: bad name length %d.", n); - goto rollback; - } + return (value != NULL); +} - DEBUG_printf("2ippReadIO: name length=%d", n); - if (n && parent) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid named IPP attribute in collection."), 1); - DEBUG_puts("1ippReadIO: bad attribute name in collection."); - goto rollback; - } - else if (n == 0 && tag != IPP_TAG_MEMBERNAME && tag != IPP_TAG_END_COLLECTION) - { - // More values for current attribute... - if (ipp->current == NULL) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP attribute has no name."), 1); - DEBUG_puts("1ippReadIO: Attribute without name and no current."); - goto rollback; - } +// +// 'ippSetDate()' - Set a dateTime value in an attribute. +// +// The "ipp" parameter refers to an IPP message previously created using +// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. +// +// The "attr" parameter may be modified as a result of setting the value. +// +// The "element" parameter specifies which value to set from 0 to +// @code ippGetCount(attr)@. +// +// @since CUPS 1.6/macOS 10.8@ +// - attr = ipp->current; - value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_CUPS_MASK); +int // O - 1 on success, 0 on failure +ippSetDate(ipp_t *ipp, // I - IPP message + ipp_attribute_t **attr, // IO - IPP attribute + int element, // I - Value number (0-based) + const ipp_uchar_t *datevalue)// I - dateTime value +{ + _ipp_value_t *value; // Current value - // Make sure we aren't adding a new value of a different type... - if (value_tag == IPP_TAG_ZERO) - { - // Setting the value of a collection member... - attr->value_tag = tag; - } - else if (value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_NAMELANG || (value_tag >= IPP_TAG_TEXT && value_tag <= IPP_TAG_MIMETYPE)) - { - // String values can sometimes come across in different forms; accept sets of differing values... - if (tag != IPP_TAG_TEXTLANG && tag != IPP_TAG_NAMELANG && (tag < IPP_TAG_TEXT || tag > IPP_TAG_MIMETYPE) && tag != IPP_TAG_NOVALUE) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP 1setOf attribute with incompatible value tags."), 1); - DEBUG_printf("1ippReadIO: 1setOf value tag %x(%s) != %x(%s)", value_tag, ippTagString(value_tag), tag, ippTagString(tag)); - goto rollback; - } - if (value_tag != tag) - { - DEBUG_printf("1ippReadIO: Converting %s attribute from %s to %s.", attr->name, ippTagString(value_tag), ippTagString(tag)); - if (!ippSetValueTag(ipp, &attr, tag)) - goto rollback; - } - } - else if (value_tag == IPP_TAG_INTEGER || value_tag == IPP_TAG_RANGE) - { - // Integer and rangeOfInteger values can sometimes be mixed; accept sets of differing values... - if (tag != IPP_TAG_INTEGER && tag != IPP_TAG_RANGE) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP 1setOf attribute with incompatible value tags."), 1); - DEBUG_printf("1ippReadIO: 1setOf value tag %x(%s) != %x(%s)", value_tag, ippTagString(value_tag), tag, ippTagString(tag)); - goto rollback; - } + // Range check input... + if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_DATE && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values || !datevalue) + return (0); - if (value_tag == IPP_TAG_INTEGER && tag == IPP_TAG_RANGE) - { - // Convert integer values to rangeOfInteger values... - DEBUG_printf("1ippReadIO: Converting %s attribute to rangeOfInteger.", attr->name); - ippSetValueTag(ipp, &attr, IPP_TAG_RANGE); - } - } - else if (value_tag != tag) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP 1setOf attribute with incompatible value tags."), 1); - DEBUG_printf("1ippReadIO: value tag %x(%s) != %x(%s)", value_tag, ippTagString(value_tag), tag, ippTagString(tag)); - goto rollback; - } - - // Finally, reallocate the attribute array as needed... - if ((value = ipp_set_value(ipp, &attr, attr->num_values)) == NULL) - goto rollback; - } - else if (tag == IPP_TAG_MEMBERNAME) - { - // Name must be length 0! - if (n) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP member name is not empty."), 1); - DEBUG_puts("1ippReadIO: member name not empty."); - goto rollback; - } - else if (!parent) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP member attribute outside of collection."), 1); - DEBUG_puts("1ippReadIO: member attribute outside of collection."); - goto rollback; - } - - if (ipp->current) - ipp->prev = ipp->current; + // Set the value and return... + if ((value = ipp_set_value(ipp, attr, element)) != NULL) + { + (*attr)->value_tag = IPP_TAG_DATE; + memcpy(value->date, datevalue, sizeof(value->date)); + } - attr = ipp->current = ipp_add_attr(ipp, NULL, ipp->curtag, IPP_TAG_ZERO, 1); - if (!attr) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to allocate IPP attribute."), 1); - DEBUG_puts("1ippReadIO: unable to allocate attribute."); - goto rollback; - } + return (value != NULL); +} - DEBUG_printf("2ippReadIO: membername, ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev); - value = attr->values; - } - else if (tag != IPP_TAG_END_COLLECTION) - { - // New attribute; read the name and add it... - if ((*cb)(src, buffer, (size_t)n) < n) - { - DEBUG_puts("1ippReadIO: unable to read name."); - goto rollback; - } +// +// 'ippSetGroupTag()' - Set the group tag of an attribute. +// +// The "ipp" parameter refers to an IPP message previously created using +// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. +// +// The "attr" parameter may be modified as a result of setting the value. +// +// The "group" parameter specifies the IPP attribute group tag: none +// (`IPP_TAG_ZERO`, for member attributes), document (`IPP_TAG_DOCUMENT`), +// event notification (`IPP_TAG_EVENT_NOTIFICATION`), operation +// (`IPP_TAG_OPERATION`), printer (`IPP_TAG_PRINTER`), subscription +// (`IPP_TAG_SUBSCRIPTION`), or unsupported (`IPP_TAG_UNSUPPORTED_GROUP`). +// +// @since CUPS 1.6/macOS 10.8@ +// - buffer[n] = '\0'; +int // O - 1 on success, 0 on failure +ippSetGroupTag( + ipp_t *ipp, // I - IPP message + ipp_attribute_t **attr, // IO - Attribute + ipp_tag_t group_tag) // I - Group tag +{ + // Range check input - group tag must be 0x01 to 0x0F, per RFC 8011... + if (!ipp || !attr || !*attr || group_tag < IPP_TAG_ZERO || group_tag == IPP_TAG_END || group_tag >= IPP_TAG_UNSUPPORTED_VALUE) + return (0); - if (ipp->current) - ipp->prev = ipp->current; + // Set the group tag and return... + (*attr)->group_tag = group_tag; - if ((attr = ipp->current = ipp_add_attr(ipp, (char *)buffer, ipp->curtag, tag, 1)) == NULL) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to allocate IPP attribute."), 1); - DEBUG_puts("1ippReadIO: unable to allocate attribute."); - goto rollback; - } + return (1); +} - DEBUG_printf("2ippReadIO: name=\"%s\", ipp->current=%p, ipp->prev=%p", buffer, (void *)ipp->current, (void *)ipp->prev); - value = attr->values; - } - else - { - attr = NULL; - value = NULL; - } +// +// 'ippSetInteger()' - Set an integer or enum value in an attribute. +// +// The "ipp" parameter refers to an IPP message previously created using +// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. +// +// The "attr" parameter may be modified as a result of setting the value. +// +// The "element" parameter specifies which value to set from 0 to +// @code ippGetCount(attr)@. +// +// @since CUPS 1.6/macOS 10.8@ +// - if ((*cb)(src, buffer, 2) < 2) - { - DEBUG_puts("1ippReadIO: unable to read value length."); - goto rollback; - } +int // O - 1 on success, 0 on failure +ippSetInteger(ipp_t *ipp, // I - IPP message + ipp_attribute_t **attr, // IO - IPP attribute + int element, // I - Value number (0-based) + int intvalue) // I - Integer/enum value +{ + _ipp_value_t *value; // Current value - n = (buffer[0] << 8) | buffer[1]; - DEBUG_printf("2ippReadIO: value length=%d", n); - if (n >= IPP_BUF_SIZE) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP value larger than 32767 bytes."), 1); - DEBUG_printf("1ippReadIO: bad value length %d.", n); - goto rollback; - } + // Range check input... + if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_INTEGER && (*attr)->value_tag != IPP_TAG_ENUM && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values) + return (0); - switch (tag) - { - case IPP_TAG_INTEGER : - case IPP_TAG_ENUM : - if (n != 4) - { - if (tag == IPP_TAG_INTEGER) - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP integer value not 4 bytes."), 1); - else - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP enum value not 4 bytes."), 1); - DEBUG_printf("1ippReadIO: bad integer value length %d.", n); - goto rollback; - } + // Set the value and return... + if ((value = ipp_set_value(ipp, attr, element)) != NULL) + { + if ((*attr)->value_tag != IPP_TAG_ENUM) + (*attr)->value_tag = IPP_TAG_INTEGER; - if ((*cb)(src, buffer, 4) < 4) - { - DEBUG_puts("1ippReadIO: Unable to read integer value."); - goto rollback; - } + value->integer = intvalue; + } - n = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; + return (value != NULL); +} - if (attr->value_tag == IPP_TAG_RANGE) - value->range.lower = value->range.upper = n; - else - value->integer = n; - break; - case IPP_TAG_BOOLEAN : - if (n != 1) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP boolean value not 1 byte."), 1); - DEBUG_printf("1ippReadIO: bad boolean value length %d.", n); - goto rollback; - } +// +// 'ippSetName()' - Set the name of an attribute. +// +// The "ipp" parameter refers to an IPP message previously created using +// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. +// +// The "attr" parameter may be modified as a result of setting the value. +// +// @since CUPS 1.6/macOS 10.8@ +// - if ((*cb)(src, buffer, 1) < 1) - { - DEBUG_puts("1ippReadIO: Unable to read boolean value."); - goto rollback; - } +int // O - 1 on success, 0 on failure +ippSetName(ipp_t *ipp, // I - IPP message + ipp_attribute_t **attr, // IO - IPP attribute + const char *name) // I - Attribute name +{ + char *temp; // Temporary name value - value->boolean = (char)buffer[0]; - break; - case IPP_TAG_UNSUPPORTED_VALUE : - case IPP_TAG_DEFAULT : - case IPP_TAG_UNKNOWN : - case IPP_TAG_NOVALUE : - case IPP_TAG_NOTSETTABLE : - case IPP_TAG_DELETEATTR : - case IPP_TAG_ADMINDEFINE : - // These value types are not supposed to have values, however - // some vendors (Brother) do not implement IPP correctly and so - // we need to map non-empty values to text... - if (attr->value_tag == tag) - { - if (n == 0) - break; + // Range check input... + if (!ipp || !attr || !*attr) + return (0); - attr->value_tag = IPP_TAG_TEXT; - } + // Set the value and return... + if ((temp = _cupsStrAlloc(name)) != NULL) + { + if ((*attr)->name) + _cupsStrFree((*attr)->name); - case IPP_TAG_TEXT : - case IPP_TAG_NAME : - case IPP_TAG_RESERVED_STRING : - case IPP_TAG_KEYWORD : - case IPP_TAG_URI : - case IPP_TAG_URISCHEME : - case IPP_TAG_CHARSET : - case IPP_TAG_LANGUAGE : - case IPP_TAG_MIMETYPE : - if (n > 0) - { - if ((*cb)(src, buffer, (size_t)n) < n) - { - DEBUG_puts("1ippReadIO: unable to read string value."); - goto rollback; - } - } + (*attr)->name = temp; + } - buffer[n] = '\0'; - value->string.text = _cupsStrAlloc((char *)buffer); - DEBUG_printf("2ippReadIO: value=\"%s\"", value->string.text); - break; + return (temp != NULL); +} - case IPP_TAG_DATE : - if (n != 11) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP date value not 11 bytes."), 1); - DEBUG_printf("1ippReadIO: bad date value length %d.", n); - goto rollback; - } - if ((*cb)(src, value->date, 11) < 11) - { - DEBUG_puts("1ippReadIO: Unable to read date value."); - goto rollback; - } - break; +// +// 'ippSetOctetString()' - Set an octetString value in an IPP attribute. +// +// The "ipp" parameter refers to an IPP message previously created using +// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. +// +// The "attr" parameter may be modified as a result of setting the value. +// +// The "element" parameter specifies which value to set from 0 to +// @code ippGetCount(attr)@. +// +// @since CUPS 1.7/macOS 10.9@ +// - case IPP_TAG_RESOLUTION : - if (n != 9) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP resolution value not 9 bytes."), 1); - DEBUG_printf("1ippReadIO: bad resolution value length %d.", n); - goto rollback; - } - - if ((*cb)(src, buffer, 9) < 9) - { - DEBUG_puts("1ippReadIO: Unable to read resolution value."); - goto rollback; - } +int // O - 1 on success, 0 on failure +ippSetOctetString( + ipp_t *ipp, // I - IPP message + ipp_attribute_t **attr, // IO - IPP attribute + int element, // I - Value number (0-based) + const void *data, // I - Pointer to octetString data + int datalen) // I - Length of octetString data +{ + _ipp_value_t *value; // Current value - value->resolution.xres = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; - value->resolution.yres = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; - value->resolution.units = (ipp_res_t)buffer[8]; - break; - case IPP_TAG_RANGE : - if (n != 8) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP rangeOfInteger value not 8 bytes."), 1); - DEBUG_printf("1ippReadIO: bad rangeOfInteger value length %d.", n); - goto rollback; - } + // Range check input... + if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_STRING && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN && (*attr)->value_tag != IPP_TAG_EXTENSION) || element < 0 || element > (*attr)->num_values || datalen < 0 || datalen > IPP_MAX_LENGTH) + return (0); - if ((*cb)(src, buffer, 8) < 8) - { - DEBUG_puts("1ippReadIO: Unable to read range value."); - goto rollback; - } + // Set the value and return... + if ((value = ipp_set_value(ipp, attr, element)) != NULL) + { + if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST) + { + // Just copy the pointer... + value->unknown.data = (void *)data; + value->unknown.length = datalen; + } + else + { + // Copy the data... + (*attr)->value_tag = IPP_TAG_STRING; - value->range.lower = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; - value->range.upper = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; - break; + if (value->unknown.data) + { + // Free previous data... + free(value->unknown.data); - case IPP_TAG_TEXTLANG : - case IPP_TAG_NAMELANG : - if (n < 4) - { - if (tag == IPP_TAG_TEXTLANG) - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP textWithLanguage value less than minimum 4 bytes."), 1); - else - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP nameWithLanguage value less than minimum 4 bytes."), 1); - DEBUG_printf("1ippReadIO: bad stringWithLanguage value length %d.", n); - goto rollback; - } + value->unknown.data = NULL; + value->unknown.length = 0; + } - if ((*cb)(src, buffer, (size_t)n) < n) - { - DEBUG_puts("1ippReadIO: Unable to read string w/language value."); - goto rollback; - } + if (datalen > 0) + { + void *temp; // Temporary data pointer - bufptr = buffer; - bufend = buffer + n; + if ((temp = malloc((size_t)datalen)) != NULL) + { + memcpy(temp, data, (size_t)datalen); - // textWithLanguage and nameWithLanguage are composite - // values: - // - // language-length - // language - // text-length - // text - n = (bufptr[0] << 8) | bufptr[1]; + value->unknown.data = temp; + value->unknown.length = datalen; + } + else + { + return (0); + } + } + } + } - if ((bufptr + 2 + n + 2) > bufend || n >= (int)sizeof(string)) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP language length overflows value."), 1); - DEBUG_printf("1ippReadIO: bad language value length %d.", n); - goto rollback; - } - else if (n >= IPP_MAX_LANGUAGE) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP language length too large."), 1); - DEBUG_printf("1ippReadIO: bad language value length %d.", n); - goto rollback; - } + return (value != NULL); +} - memcpy(string, bufptr + 2, (size_t)n); - string[n] = '\0'; - value->string.language = _cupsStrAlloc((char *)string); +// +// 'ippSetOperation()' - Set the operation ID in an IPP request message. +// +// The "ipp" parameter refers to an IPP message previously created using +// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. +// +// @since CUPS 1.6/macOS 10.8@ +// - bufptr += 2 + n; - n = (bufptr[0] << 8) | bufptr[1]; +int // O - 1 on success, 0 on failure +ippSetOperation(ipp_t *ipp, // I - IPP request message + ipp_op_t op) // I - Operation ID +{ + // Range check input... + if (!ipp) + return (0); - if ((bufptr + 2 + n) > bufend) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP string length overflows value."), 1); - DEBUG_printf("1ippReadIO: bad string value length %d.", n); - goto rollback; - } + // Set the operation and return... + ipp->request.op.operation_id = op; - bufptr[2 + n] = '\0'; - value->string.text = _cupsStrAlloc((char *)bufptr + 2); - break; + return (1); +} - case IPP_TAG_BEGIN_COLLECTION : - // Oh boy, here comes a collection value, so read it... - value->collection = ippNew(); - if (n > 0) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP begCollection value not 0 bytes."), 1); - DEBUG_puts("1ippReadIO: begCollection tag with value length > 0."); - goto rollback; - } +// +// 'ippSetRange()' - Set a rangeOfInteger value in an attribute. +// +// The "ipp" parameter refers to an IPP message previously created using +// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. +// +// The "attr" parameter may be modified as a result of setting the value. +// +// The "element" parameter specifies which value to set from 0 to +// @code ippGetCount(attr)@. +// +// @since CUPS 1.6/macOS 10.8@ +// - if (ippReadIO(src, cb, 1, ipp, value->collection) == IPP_STATE_ERROR) - { - DEBUG_puts("1ippReadIO: Unable to read collection value."); - goto rollback; - } - break; +int // O - 1 on success, 0 on failure +ippSetRange(ipp_t *ipp, // I - IPP message + ipp_attribute_t **attr, // IO - IPP attribute + int element, // I - Value number (0-based) + int lowervalue, // I - Lower bound for range + int uppervalue) // I - Upper bound for range +{ + _ipp_value_t *value; // Current value - case IPP_TAG_END_COLLECTION : - if (n > 0) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP endCollection value not 0 bytes."), 1); - DEBUG_puts("1ippReadIO: endCollection tag with value length > 0."); - goto rollback; - } - _cupsBufferRelease((char *)buffer); + // Range check input... + if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_RANGE && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values || lowervalue > uppervalue) + return (0); - DEBUG_puts("1ippReadIO: endCollection tag..."); - return (ipp->state = IPP_STATE_DATA); + // Set the value and return... + if ((value = ipp_set_value(ipp, attr, element)) != NULL) + { + (*attr)->value_tag = IPP_TAG_RANGE; + value->range.lower = lowervalue; + value->range.upper = uppervalue; + } - case IPP_TAG_MEMBERNAME : - // The value the name of the member in the collection, which we need to carry over... - if (!attr) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP memberName with no attribute."), 1); - DEBUG_puts("1ippReadIO: Member name without attribute."); - goto rollback; - } - else if (n == 0) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP memberName value is empty."), 1); - DEBUG_puts("1ippReadIO: Empty member name value."); - goto rollback; - } - else if ((*cb)(src, buffer, (size_t)n) < n) - { - DEBUG_puts("1ippReadIO: Unable to read member name value."); - goto rollback; - } + return (value != NULL); +} - buffer[n] = '\0'; - attr->name = _cupsStrAlloc((char *)buffer); - // Since collection members are encoded differently than regular - // attributes, make sure we don't start with an empty value... - attr->num_values --; +// +// 'ippSetRequestId()' - Set the request ID in an IPP message. +// +// The "ipp" parameter refers to an IPP message previously created using +// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. +// +// The @code request_id@ parameter must be greater than 0. +// +// @since CUPS 1.6/macOS 10.8@ +// - DEBUG_printf("2ippReadIO: member name=\"%s\"", attr->name); - break; +int // O - 1 on success, 0 on failure +ippSetRequestId(ipp_t *ipp, // I - IPP message + int request_id) // I - Request ID +{ + // Range check input; not checking request_id values since ipptool wants to + // send invalid values for conformance testing and a bad request_id does not + // affect the encoding of a message... + if (!ipp) + return (0); - case IPP_TAG_STRING : - default : // Other unsupported values - if (tag == IPP_TAG_STRING && n > IPP_MAX_LENGTH) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP octetString length too large."), 1); - DEBUG_printf("1ippReadIO: bad octetString value length %d.", n); - goto rollback; - } + // Set the request ID and return... + ipp->request.any.request_id = request_id; - value->unknown.length = n; - - if (n > 0) - { - if ((value->unknown.data = malloc((size_t)n)) == NULL) - { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to allocate IPP attribute."), 1); - DEBUG_puts("1ippReadIO: Unable to allocate value"); - goto rollback; - } - - if ((*cb)(src, value->unknown.data, (size_t)n) < n) - { - DEBUG_puts("1ippReadIO: Unable to read unsupported value."); - goto rollback; - } - } - else - { - value->unknown.data = NULL; - } - break; - } - - // If blocking is disabled, stop here... - if (!blocking) - break; - } - break; - - case IPP_STATE_DATA : - break; - - default : - break; // anti-compiler-warning-code - } - - DEBUG_printf("1ippReadIO: returning ipp->state=%d.", ipp->state); - _cupsBufferRelease((char *)buffer); - - return (ipp->state); - - // If we get here, there was an error that required us to roll back the last - // attribute read in order to keep the IPP message valid... - rollback: - - _cupsBufferRelease((char *)buffer); - - if (attr) - ippDeleteAttribute(ipp, attr); - - return (IPP_STATE_ERROR); -} - - -// -// 'ippRestore()' - Restore a previously saved find position. -// -// @since CUPS 2.5@ -// - -void -ippRestore(ipp_t *ipp) // I - IPP message -{ - if (ipp) - { - if (ipp->find > ipp->fstack) - ipp->find --; - else - ipp->find = NULL; - } -} - - -// -// 'ippSave()' - Save the current find position. -// -// @since CUPS 2.5@ -// - -void -ippSave(ipp_t *ipp) // I - IPP message -{ - if (ipp && ipp->find < (ipp->fstack + _IPP_MAX_FIND)) - { - ipp->find[1] = ipp->find[0]; - ipp->find ++; - } -} + return (1); +} // -// 'ippSetBoolean()' - Set a boolean value in an attribute. +// 'ippSetResolution()' - Set a resolution value in an attribute. // // The "ipp" parameter refers to an IPP message previously created using // the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. @@ -3282,70 +3082,82 @@ ippSave(ipp_t *ipp) // I - IPP message // int // O - 1 on success, 0 on failure -ippSetBoolean(ipp_t *ipp, // I - IPP message - ipp_attribute_t **attr, // IO - IPP attribute - int element, // I - Value number (0-based) - int boolvalue)// I - Boolean value +ippSetResolution( + ipp_t *ipp, // I - IPP message + ipp_attribute_t **attr, // IO - IPP attribute + int element, // I - Value number (0-based) + ipp_res_t unitsvalue, // I - Resolution units + int xresvalue, // I - Horizontal/cross feed resolution + int yresvalue) // I - Vertical/feed resolution { _ipp_value_t *value; // Current value // Range check input... - if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BOOLEAN || element < 0 || element > (*attr)->num_values) + if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_RESOLUTION && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values || xresvalue <= 0 || yresvalue <= 0 || unitsvalue < IPP_RES_PER_INCH || unitsvalue > IPP_RES_PER_CM) return (0); // Set the value and return... if ((value = ipp_set_value(ipp, attr, element)) != NULL) - value->boolean = (char)boolvalue; + { + (*attr)->value_tag = IPP_TAG_RESOLUTION; + value->resolution.units = unitsvalue; + value->resolution.xres = xresvalue; + value->resolution.yres = yresvalue; + } return (value != NULL); } // -// 'ippSetCollection()' - Set a collection value in an attribute. +// 'ippSetState()' - Set the current state of the IPP message. // -// The "ipp" parameter refers to an IPP message previously created using -// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. +// @since CUPS 1.6/macOS 10.8@ // -// The "attr" parameter may be modified as a result of setting the value. + +int // O - 1 on success, 0 on failure +ippSetState(ipp_t *ipp, // I - IPP message + ipp_state_t state) // I - IPP state value +{ + // Range check input... + if (!ipp) + return (0); + + // Set the state and return... + ipp->state = state; + ipp->current = NULL; + + return (1); +} + + // -// The "element" parameter specifies which value to set from 0 to -// @code ippGetCount(attr)@. +// 'ippSetStatusCode()' - Set the status code in an IPP response or event message. +// +// The "ipp" parameter refers to an IPP message previously created using +// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. // // @since CUPS 1.6/macOS 10.8@ // -int // O - 1 on success, 0 on failure -ippSetCollection( - ipp_t *ipp, // I - IPP message - ipp_attribute_t **attr, // IO - IPP attribute - int element, // I - Value number (0-based) - ipp_t *colvalue) // I - Collection value +int // O - 1 on success, 0 on failure +ippSetStatusCode(ipp_t *ipp, // I - IPP response or event message + ipp_status_t status) // I - Status code { - _ipp_value_t *value; // Current value - - // Range check input... - if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BEGIN_COLLECTION || element < 0 || element > (*attr)->num_values || !colvalue) + if (!ipp) return (0); - // Set the value and return... - if ((value = ipp_set_value(ipp, attr, element)) != NULL) - { - if (value->collection) - ippDelete(value->collection); - - value->collection = colvalue; - colvalue->use ++; - } + // Set the status code and return... + ipp->request.status.status_code = status; - return (value != NULL); + return (1); } // -// 'ippSetDate()' - Set a dateTime value in an attribute. +// 'ippSetString()' - Set a string value in an attribute. // // The "ipp" parameter refers to an IPP message previously created using // the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. @@ -3359,23 +3171,49 @@ ippSetCollection( // int // O - 1 on success, 0 on failure -ippSetDate(ipp_t *ipp, // I - IPP message - ipp_attribute_t **attr, // IO - IPP attribute - int element, // I - Value number (0-based) - const ipp_uchar_t *datevalue)// I - dateTime value +ippSetString(ipp_t *ipp, // I - IPP message + ipp_attribute_t **attr, // IO - IPP attribute + int element, // I - Value number (0-based) + const char *strvalue) // I - String value { + char *temp; // Temporary string _ipp_value_t *value; // Current value + ipp_tag_t value_tag; // Value tag // Range check input... - if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_DATE && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values || !datevalue) + if (attr && *attr) + value_tag = (*attr)->value_tag & IPP_TAG_CUPS_MASK; + else + value_tag = IPP_TAG_ZERO; + + if (!ipp || !attr || !*attr || (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG && value_tag != IPP_TAG_NAMELANG && value_tag != IPP_TAG_NOVALUE && value_tag != IPP_TAG_UNKNOWN) || value_tag > IPP_TAG_MIMETYPE || element < 0 || element > (*attr)->num_values || !strvalue) return (0); // Set the value and return... if ((value = ipp_set_value(ipp, attr, element)) != NULL) { - (*attr)->value_tag = IPP_TAG_DATE; - memcpy(value->date, datevalue, sizeof(value->date)); + if (value_tag == IPP_TAG_NOVALUE || value_tag == IPP_TAG_UNKNOWN) + (*attr)->value_tag = IPP_TAG_KEYWORD; + + if (element > 0) + value->string.language = (*attr)->values[0].string.language; + + if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST) + { + value->string.text = (char *)strvalue; + } + else if ((temp = _cupsStrAlloc(strvalue)) != NULL) + { + if (value->string.text) + _cupsStrFree(value->string.text); + + value->string.text = temp; + } + else + { + return (0); + } } return (value != NULL); @@ -3383,41 +3221,45 @@ ippSetDate(ipp_t *ipp, // I - IPP message // -// 'ippSetGroupTag()' - Set the group tag of an attribute. +// 'ippSetStringf()' - Set a formatted string value of an attribute. // // The "ipp" parameter refers to an IPP message previously created using // the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. // // The "attr" parameter may be modified as a result of setting the value. // -// The "group" parameter specifies the IPP attribute group tag: none -// (`IPP_TAG_ZERO`, for member attributes), document (`IPP_TAG_DOCUMENT`), -// event notification (`IPP_TAG_EVENT_NOTIFICATION`), operation -// (`IPP_TAG_OPERATION`), printer (`IPP_TAG_PRINTER`), subscription -// (`IPP_TAG_SUBSCRIPTION`), or unsupported (`IPP_TAG_UNSUPPORTED_GROUP`). +// The "element" parameter specifies which value to set from 0 to +// @code ippGetCount(attr)@. // -// @since CUPS 1.6/macOS 10.8@ +// The "format" parameter uses formatting characters compatible with the +// printf family of standard functions. Additional arguments follow it as +// needed. The formatted string is truncated as needed to the maximum length of +// the corresponding value type. +// +// @since CUPS 1.7/macOS 10.9@ // int // O - 1 on success, 0 on failure -ippSetGroupTag( - ipp_t *ipp, // I - IPP message - ipp_attribute_t **attr, // IO - Attribute - ipp_tag_t group_tag) // I - Group tag +ippSetStringf(ipp_t *ipp, // I - IPP message + ipp_attribute_t **attr, // IO - IPP attribute + int element, // I - Value number (0-based) + const char *format, // I - Printf-style format string + ...) // I - Additional arguments as needed { - // Range check input - group tag must be 0x01 to 0x0F, per RFC 8011... - if (!ipp || !attr || !*attr || group_tag < IPP_TAG_ZERO || group_tag == IPP_TAG_END || group_tag >= IPP_TAG_UNSUPPORTED_VALUE) - return (0); + int ret; // Return value + va_list ap; // Pointer to additional arguments - // Set the group tag and return... - (*attr)->group_tag = group_tag; - return (1); + va_start(ap, format); + ret = ippSetStringfv(ipp, attr, element, format, ap); + va_end(ap); + + return (ret); } // -// 'ippSetInteger()' - Set an integer or enum value in an attribute. +// 'ippSetStringf()' - Set a formatted string value of an attribute. // // The "ipp" parameter refers to an IPP message previously created using // the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. @@ -3427,692 +3269,834 @@ ippSetGroupTag( // The "element" parameter specifies which value to set from 0 to // @code ippGetCount(attr)@. // -// @since CUPS 1.6/macOS 10.8@ +// The "format" parameter uses formatting characters compatible with the +// printf family of standard functions. Additional arguments follow it as +// needed. The formatted string is truncated as needed to the maximum length of +// the corresponding value type. +// +// @since CUPS 1.7/macOS 10.9@ // int // O - 1 on success, 0 on failure -ippSetInteger(ipp_t *ipp, // I - IPP message - ipp_attribute_t **attr, // IO - IPP attribute - int element, // I - Value number (0-based) - int intvalue) // I - Integer/enum value +ippSetStringfv(ipp_t *ipp, // I - IPP message + ipp_attribute_t **attr, // IO - IPP attribute + int element, // I - Value number (0-based) + const char *format, // I - Printf-style format string + va_list ap) // I - Pointer to additional arguments { - _ipp_value_t *value; // Current value + ipp_tag_t value_tag; // Value tag + char buffer[IPP_MAX_TEXT + 4]; + // Formatted text string + ssize_t bytes, // Length of formatted value + max_bytes; // Maximum number of bytes for value // Range check input... - if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_INTEGER && (*attr)->value_tag != IPP_TAG_ENUM && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values) - return (0); + if (attr && *attr) + value_tag = (*attr)->value_tag & IPP_TAG_CUPS_MASK; + else + value_tag = IPP_TAG_ZERO; - // Set the value and return... - if ((value = ipp_set_value(ipp, attr, element)) != NULL) + if (!ipp || !attr || !*attr || (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG && value_tag != IPP_TAG_NAMELANG && value_tag != IPP_TAG_NOVALUE && value_tag != IPP_TAG_UNKNOWN) || value_tag > IPP_TAG_MIMETYPE || !format) + return (0); + + // Format the string... + if (!strcmp(format, "%s")) { - if ((*attr)->value_tag != IPP_TAG_ENUM) - (*attr)->value_tag = IPP_TAG_INTEGER; + // Optimize the simple case... + const char *s = va_arg(ap, char *); - value->integer = intvalue; + if (!s) + s = "(null)"; + + bytes = (ssize_t)strlen(s); + cupsCopyString(buffer, s, sizeof(buffer)); + } + else + { + // Do a full formatting of the message... + if ((bytes = vsnprintf(buffer, sizeof(buffer), format, ap)) < 0) + return (0); } - return (value != NULL); -} + // Limit the length of the string... + switch (value_tag) + { + default : + case IPP_TAG_TEXT : + case IPP_TAG_TEXTLANG : + max_bytes = IPP_MAX_TEXT; + break; + case IPP_TAG_NAME : + case IPP_TAG_NAMELANG : + max_bytes = IPP_MAX_NAME; + break; -// -// 'ippSetName()' - Set the name of an attribute. -// -// The "ipp" parameter refers to an IPP message previously created using -// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. -// -// The "attr" parameter may be modified as a result of setting the value. -// -// @since CUPS 1.6/macOS 10.8@ -// + case IPP_TAG_CHARSET : + max_bytes = IPP_MAX_CHARSET; + break; -int // O - 1 on success, 0 on failure -ippSetName(ipp_t *ipp, // I - IPP message - ipp_attribute_t **attr, // IO - IPP attribute - const char *name) // I - Attribute name -{ - char *temp; // Temporary name value + case IPP_TAG_NOVALUE : + case IPP_TAG_UNKNOWN : + case IPP_TAG_KEYWORD : + max_bytes = IPP_MAX_KEYWORD; + break; + + case IPP_TAG_LANGUAGE : + max_bytes = IPP_MAX_LANGUAGE; + break; + case IPP_TAG_MIMETYPE : + max_bytes = IPP_MAX_MIMETYPE; + break; - // Range check input... - if (!ipp || !attr || !*attr) - return (0); + case IPP_TAG_URI : + max_bytes = IPP_MAX_URI; + break; - // Set the value and return... - if ((temp = _cupsStrAlloc(name)) != NULL) + case IPP_TAG_URISCHEME : + max_bytes = IPP_MAX_URISCHEME; + break; + } + + if (bytes >= max_bytes) { - if ((*attr)->name) - _cupsStrFree((*attr)->name); + char *bufmax, // Buffer at max_bytes + *bufptr; // Pointer into buffer - (*attr)->name = temp; + bufptr = buffer + strlen(buffer) - 1; + bufmax = buffer + max_bytes - 1; + + while (bufptr > bufmax) + { + if (*bufptr & 0x80) + { + while ((*bufptr & 0xc0) == 0x80 && bufptr > buffer) + bufptr --; + } + + bufptr --; + } + + *bufptr = '\0'; } - return (temp != NULL); + // Set the formatted string and return... + return (ippSetString(ipp, attr, element, buffer)); } // -// 'ippSetOctetString()' - Set an octetString value in an IPP attribute. +// 'ippSetValueTag()' - Set the value tag of an attribute. // // The "ipp" parameter refers to an IPP message previously created using // the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. // // The "attr" parameter may be modified as a result of setting the value. // -// The "element" parameter specifies which value to set from 0 to -// @code ippGetCount(attr)@. +// Integer (`IPP_TAG_INTEGER`) values can be promoted to rangeOfInteger +// (`IPP_TAG_RANGE`) values, the various string tags can be promoted to name +// (`IPP_TAG_NAME`) or nameWithLanguage (`IPP_TAG_NAMELANG`) values, text +// (`IPP_TAG_TEXT`) values can be promoted to textWithLanguage +// (`IPP_TAG_TEXTLANG`) values, and all values can be demoted to the various +// out-of-band value tags such as no-value (`IPP_TAG_NOVALUE`). All other +// changes will be rejected. // -// @since CUPS 1.7/macOS 10.9@ +// Promoting a string attribute to nameWithLanguage or textWithLanguage adds the language +// code in the "attributes-natural-language" attribute or, if not present, the language +// code for the current locale. +// +// @since CUPS 1.6/macOS 10.8@ // int // O - 1 on success, 0 on failure -ippSetOctetString( - ipp_t *ipp, // I - IPP message +ippSetValueTag( + ipp_t *ipp, // I - IPP message ipp_attribute_t **attr, // IO - IPP attribute - int element, // I - Value number (0-based) - const void *data, // I - Pointer to octetString data - int datalen) // I - Length of octetString data + ipp_tag_t value_tag) // I - Value tag { + int i; // Looping var _ipp_value_t *value; // Current value + int integer; // Current integer value + cups_lang_t *language; // Current language + char code[32]; // Language code + ipp_tag_t temp_tag; // Temporary value tag // Range check input... - if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_STRING && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN && (*attr)->value_tag != IPP_TAG_EXTENSION) || element < 0 || element > (*attr)->num_values || datalen < 0 || datalen > IPP_MAX_LENGTH) + if (!ipp || !attr || !*attr) return (0); - // Set the value and return... - if ((value = ipp_set_value(ipp, attr, element)) != NULL) + // If there is no change, return immediately... + temp_tag = (ipp_tag_t)((int)((*attr)->value_tag) & IPP_TAG_CUPS_MASK); + + if (value_tag == temp_tag) + return (1); + + // Otherwise implement changes as needed... + switch (value_tag) { - if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST) - { - // Just copy the pointer... - value->unknown.data = (void *)data; - value->unknown.length = datalen; - } - else - { - // Copy the data... - (*attr)->value_tag = IPP_TAG_STRING; + case IPP_TAG_UNSUPPORTED_VALUE : + case IPP_TAG_DEFAULT : + case IPP_TAG_UNKNOWN : + case IPP_TAG_NOVALUE : + case IPP_TAG_NOTSETTABLE : + case IPP_TAG_DELETEATTR : + case IPP_TAG_ADMINDEFINE : + // Free any existing values... + if ((*attr)->num_values > 0) + ipp_free_values(*attr, 0, (*attr)->num_values); - if (value->unknown.data) - { - // Free previous data... - free(value->unknown.data); + // Set out-of-band value... + (*attr)->value_tag = value_tag; + break; - value->unknown.data = NULL; - value->unknown.length = 0; - } + case IPP_TAG_RANGE : + if (temp_tag != IPP_TAG_INTEGER) + return (0); - if (datalen > 0) - { - void *temp; // Temporary data pointer + for (i = (*attr)->num_values, value = (*attr)->values; i > 0; i --, value ++) + { + integer = value->integer; + value->range.lower = value->range.upper = integer; + } - if ((temp = malloc((size_t)datalen)) != NULL) - { - memcpy(temp, data, (size_t)datalen); + (*attr)->value_tag = IPP_TAG_RANGE; + break; - value->unknown.data = temp; - value->unknown.length = datalen; - } - else - { - return (0); - } - } - } - } + case IPP_TAG_NAME : + if (temp_tag != IPP_TAG_KEYWORD) + return (0); - return (value != NULL); -} + (*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)) + return (0); -// -// 'ippSetOperation()' - Set the operation ID in an IPP request message. -// -// The "ipp" parameter refers to an IPP message previously created using -// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. -// -// @since CUPS 1.6/macOS 10.8@ -// + if (value_tag == IPP_TAG_TEXTLANG && temp_tag != IPP_TAG_TEXT) + return (0); -int // O - 1 on success, 0 on failure -ippSetOperation(ipp_t *ipp, // I - IPP request message - ipp_op_t op) // I - Operation ID -{ - // Range check input... - if (!ipp) - return (0); + if (ipp->attrs && ipp->attrs->next && ipp->attrs->next->name && !strcmp(ipp->attrs->next->name, "attributes-natural-language") && (ipp->attrs->next->value_tag & IPP_TAG_CUPS_MASK) == IPP_TAG_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))); + } - // Set the operation and return... - ipp->request.op.operation_id = op; + for (i = (*attr)->num_values - 1, value = (*attr)->values + 1; i > 0; i --, value ++) + value->string.language = (*attr)->values[0].string.language; - return (1); -} + 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; -// -// 'ippSetRange()' - Set a rangeOfInteger value in an attribute. -// -// The "ipp" parameter refers to an IPP message previously created using -// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. -// -// The "attr" parameter may be modified as a result of setting the value. -// -// The "element" parameter specifies which value to set from 0 to -// @code ippGetCount(attr)@. -// -// @since CUPS 1.6/macOS 10.8@ -// - -int // O - 1 on success, 0 on failure -ippSetRange(ipp_t *ipp, // I - IPP message - ipp_attribute_t **attr, // IO - IPP attribute - int element, // I - Value number (0-based) - int lowervalue, // I - Lower bound for range - int uppervalue) // I - Upper bound for range -{ - _ipp_value_t *value; // Current value + case IPP_TAG_KEYWORD : + if (temp_tag == IPP_TAG_NAME || temp_tag == IPP_TAG_NAMELANG) + break; // Silently "allow" name -> keyword + return (0); - // Range check input... - if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_RANGE && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values || lowervalue > uppervalue) - return (0); + case IPP_TAG_EXTENSION : + if (temp_tag == IPP_TAG_STRING && value_tag == IPP_TAG_EXTENSION) + { + // Allow octetString -> extension + (*attr)->value_tag = value_tag; + break; + } - // Set the value and return... - if ((value = ipp_set_value(ipp, attr, element)) != NULL) - { - (*attr)->value_tag = IPP_TAG_RANGE; - value->range.lower = lowervalue; - value->range.upper = uppervalue; + default : + return (0); } - return (value != NULL); + return (1); } // -// 'ippSetRequestId()' - Set the request ID in an IPP message. +// 'ippSetVersion()' - Set the version number in an IPP message. // // The "ipp" parameter refers to an IPP message previously created using // the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. // -// The @code request_id@ parameter must be greater than 0. +// 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 -ippSetRequestId(ipp_t *ipp, // I - IPP message - int request_id) // I - Request ID +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; not checking request_id values since ipptool wants to - // send invalid values for conformance testing and a bad request_id does not - // affect the encoding of a message... - if (!ipp) + // Range check input... + if (!ipp || major < 0 || minor < 0) return (0); - // Set the request ID and return... - ipp->request.any.request_id = request_id; + // Set the version number... + ipp->request.any.version[0] = (ipp_uchar_t)major; + ipp->request.any.version[1] = (ipp_uchar_t)minor; return (1); } // -// 'ippSetResolution()' - Set a resolution value in an attribute. -// -// The "ipp" parameter refers to an IPP message previously created using -// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. -// -// The "attr" parameter may be modified as a result of setting the value. -// -// The "element" parameter specifies which value to set from 0 to -// @code ippGetCount(attr)@. -// -// @since CUPS 1.6/macOS 10.8@ +// 'ippTimeToDate()' - Convert from time in seconds to RFC 2579 format. // -int // O - 1 on success, 0 on failure -ippSetResolution( - ipp_t *ipp, // I - IPP message - ipp_attribute_t **attr, // IO - IPP attribute - int element, // I - Value number (0-based) - ipp_res_t unitsvalue, // I - Resolution units - int xresvalue, // I - Horizontal/cross feed resolution - int yresvalue) // I - Vertical/feed resolution +const ipp_uchar_t * // O - RFC-2579 date/time data +ippTimeToDate(time_t t) // I - Time in seconds { - _ipp_value_t *value; // Current value - - - // Range check input... - if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_RESOLUTION && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values || xresvalue <= 0 || yresvalue <= 0 || unitsvalue < IPP_RES_PER_INCH || unitsvalue > IPP_RES_PER_CM) - return (0); - - // Set the value and return... - if ((value = ipp_set_value(ipp, attr, element)) != NULL) - { - (*attr)->value_tag = IPP_TAG_RESOLUTION; - value->resolution.units = unitsvalue; - value->resolution.xres = xresvalue; - value->resolution.yres = yresvalue; - } - - return (value != NULL); -} - + struct tm unixdate; // UNIX unixdate/time info + ipp_uchar_t *date = _cupsGlobals()->ipp_date; + // RFC-2579 date/time data -// -// 'ippSetState()' - Set the current state of the IPP message. -// -// @since CUPS 1.6/macOS 10.8@ -// -int // O - 1 on success, 0 on failure -ippSetState(ipp_t *ipp, // I - IPP message - ipp_state_t state) // I - IPP state value -{ - // Range check input... - if (!ipp) - return (0); + // 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) + gmtime_r(&t, &unixdate); + unixdate.tm_year += 1900; - // Set the state and return... - ipp->state = state; - ipp->current = NULL; + 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 (1); + return (date); } // -// 'ippSetStatusCode()' - Set the status code in an IPP response or event message. +// 'ippValidateAttribute()' - Validate the contents of an attribute. // -// The "ipp" parameter refers to an IPP message previously created using -// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. +// 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 cupsGetErrorString@ is set to a human-readable message. // -// @since CUPS 1.6/macOS 10.8@ +// @since CUPS 1.7/macOS 10.9@ // -int // O - 1 on success, 0 on failure -ippSetStatusCode(ipp_t *ipp, // I - IPP response or event message - ipp_status_t status) // I - Status code +int // O - 1 if valid, 0 otherwise +ippValidateAttribute( + ipp_attribute_t *attr) // I - Attribute { - // Range check input... - if (!ipp) - return (0); - - // Set the status code and return... - ipp->request.status.status_code = status; - - return (1); -} - + int i, // Looping var + r; // regcomp() error code + 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 -// -// 'ippSetString()' - Set a string value in an attribute. -// -// The "ipp" parameter refers to an IPP message previously created using -// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. -// -// The "attr" parameter may be modified as a result of setting the value. -// -// The "element" parameter specifies which value to set from 0 to -// @code ippGetCount(attr)@. -// -// @since CUPS 1.6/macOS 10.8@ -// -int // O - 1 on success, 0 on failure -ippSetString(ipp_t *ipp, // I - IPP message - ipp_attribute_t **attr, // IO - IPP attribute - int element, // I - Value number (0-based) - const char *strvalue) // I - String value -{ - char *temp; // Temporary string - _ipp_value_t *value; // Current value - ipp_tag_t value_tag; // Value tag + // 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; + } - // Range check input... - if (attr && *attr) - value_tag = (*attr)->value_tag & IPP_TAG_CUPS_MASK; - else - value_tag = IPP_TAG_ZERO; + 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 (!ipp || !attr || !*attr || (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG && value_tag != IPP_TAG_NAMELANG && value_tag != IPP_TAG_NOVALUE && value_tag != IPP_TAG_UNKNOWN) || value_tag > IPP_TAG_MIMETYPE || element < 0 || element > (*attr)->num_values || !strvalue) + 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); + } - // Set the value and return... - if ((value = ipp_set_value(ipp, attr, element)) != NULL) + switch (attr->value_tag) { - if (value_tag == IPP_TAG_NOVALUE || value_tag == IPP_TAG_UNKNOWN) - (*attr)->value_tag = IPP_TAG_KEYWORD; + case IPP_TAG_INTEGER : + break; - if (element > 0) - value->string.language = (*attr)->values[0].string.language; + 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; - if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST) - { - value->string.text = (char *)strvalue; - } - else if ((temp = _cupsStrAlloc(strvalue)) != NULL) - { - if (value->string.text) - _cupsStrFree(value->string.text); - - value->string.text = temp; - } - else - { - return (0); - } - } - - return (value != NULL); -} - - -// -// 'ippSetStringf()' - Set a formatted string value of an attribute. -// -// The "ipp" parameter refers to an IPP message previously created using -// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. -// -// The "attr" parameter may be modified as a result of setting the value. -// -// The "element" parameter specifies which value to set from 0 to -// @code ippGetCount(attr)@. -// -// The "format" parameter uses formatting characters compatible with the -// printf family of standard functions. Additional arguments follow it as -// needed. The formatted string is truncated as needed to the maximum length of -// the corresponding value type. -// -// @since CUPS 1.7/macOS 10.9@ -// - -int // O - 1 on success, 0 on failure -ippSetStringf(ipp_t *ipp, // I - IPP message - ipp_attribute_t **attr, // IO - IPP attribute - int element, // I - Value number (0-based) - const char *format, // I - Printf-style format string - ...) // I - Additional arguments as needed -{ - int ret; // Return value - va_list ap; // Pointer to additional arguments - - - va_start(ap, format); - ret = ippSetStringfv(ipp, attr, element, format, ap); - va_end(ap); + 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; - return (ret); -} + 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; -// -// 'ippSetStringf()' - Set a formatted string value of an attribute. -// -// The "ipp" parameter refers to an IPP message previously created using -// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. -// -// The "attr" parameter may be modified as a result of setting the value. -// -// The "element" parameter specifies which value to set from 0 to -// @code ippGetCount(attr)@. -// -// The "format" parameter uses formatting characters compatible with the -// printf family of standard functions. Additional arguments follow it as -// needed. The formatted string is truncated as needed to the maximum length of -// the corresponding value type. -// -// @since CUPS 1.7/macOS 10.9@ -// + 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); + } -int // O - 1 on success, 0 on failure -ippSetStringfv(ipp_t *ipp, // I - IPP message - ipp_attribute_t **attr, // IO - IPP attribute - int element, // I - Value number (0-based) - const char *format, // I - Printf-style format string - va_list ap) // I - Pointer to additional arguments -{ - ipp_tag_t value_tag; // Value tag - char buffer[IPP_MAX_TEXT + 4]; - // Formatted text string - ssize_t bytes, // Length of formatted value - max_bytes; // Maximum number of bytes for value + 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); + } - // Range check input... - if (attr && *attr) - value_tag = (*attr)->value_tag & IPP_TAG_CUPS_MASK; - else - value_tag = IPP_TAG_ZERO; + 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 (!ipp || !attr || !*attr || (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG && value_tag != IPP_TAG_NAMELANG && value_tag != IPP_TAG_NOVALUE && value_tag != IPP_TAG_UNKNOWN) || value_tag > IPP_TAG_MIMETYPE || !format) - 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); + } - // Format the string... - if (!strcmp(format, "%s")) - { - // Optimize the simple case... - const char *s = va_arg(ap, char *); + 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 (!s) - s = "(null)"; + 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); + } - bytes = (ssize_t)strlen(s); - cupsCopyString(buffer, s, sizeof(buffer)); - } - else - { - // Do a full formatting of the message... - if ((bytes = vsnprintf(buffer, sizeof(buffer), format, ap)) < 0) - 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); + } - // Limit the length of the string... - switch (value_tag) - { - default : - case IPP_TAG_TEXT : - case IPP_TAG_TEXTLANG : - max_bytes = IPP_MAX_TEXT; + 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_NAME : - case IPP_TAG_NAMELANG : - max_bytes = IPP_MAX_NAME; - 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); + } - case IPP_TAG_CHARSET : - max_bytes = IPP_MAX_CHARSET; - break; + 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); + } - case IPP_TAG_NOVALUE : - case IPP_TAG_UNKNOWN : - case IPP_TAG_KEYWORD : - max_bytes = IPP_MAX_KEYWORD; + 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_LANGUAGE : - max_bytes = IPP_MAX_LANGUAGE; + 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_MIMETYPE : - max_bytes = IPP_MAX_MIMETYPE; + 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_URI : - max_bytes = IPP_MAX_URI; - 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; - case IPP_TAG_URISCHEME : - max_bytes = IPP_MAX_URISCHEME; - break; - } - - if (bytes >= max_bytes) - { - char *bufmax, // Buffer at max_bytes - *bufptr; // Pointer into buffer - - bufptr = buffer + strlen(buffer) - 1; - bufmax = buffer + max_bytes - 1; + ptr ++; + } + else if ((*ptr & 0xf0) == 0xe0) + { + if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80) + break; - while (bufptr > bufmax) - { - if (*bufptr & 0x80) - { - while ((*bufptr & 0xc0) == 0x80 && bufptr > buffer) - bufptr --; - } + ptr += 2; + } + else if ((*ptr & 0xf8) == 0xf0) + { + if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80 || (ptr[3] & 0xc0) != 0x80) + break; - bufptr --; - } + ptr += 3; + } + else if (*ptr & 0x80) + { + break; + } + else if ((*ptr < ' ' && *ptr != '\n' && *ptr != '\r' && *ptr != '\t') || *ptr == 0x7f) + { + break; + } + } - *bufptr = '\0'; - } + 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); + } + } - // Set the formatted string and return... - return (ippSetString(ipp, attr, element, buffer)); -} + 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; -// -// 'ippSetValueTag()' - Set the value tag of an attribute. -// -// The "ipp" parameter refers to an IPP message previously created using -// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. -// -// The "attr" parameter may be modified as a result of setting the value. -// -// Integer (`IPP_TAG_INTEGER`) values can be promoted to rangeOfInteger -// (`IPP_TAG_RANGE`) values, the various string tags can be promoted to name -// (`IPP_TAG_NAME`) or nameWithLanguage (`IPP_TAG_NAMELANG`) values, text -// (`IPP_TAG_TEXT`) values can be promoted to textWithLanguage -// (`IPP_TAG_TEXTLANG`) values, and all values can be demoted to the various -// out-of-band value tags such as no-value (`IPP_TAG_NOVALUE`). All other -// changes will be rejected. -// -// Promoting a string attribute to nameWithLanguage or textWithLanguage adds the language -// code in the "attributes-natural-language" attribute or, if not present, the language -// code for the current locale. -// -// @since CUPS 1.6/macOS 10.8@ -// + ptr ++; + } + else if ((*ptr & 0xf0) == 0xe0) + { + if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80) + break; -int // O - 1 on success, 0 on failure -ippSetValueTag( - ipp_t *ipp, // I - IPP message - ipp_attribute_t **attr, // IO - IPP attribute - ipp_tag_t value_tag) // I - Value tag -{ - int i; // Looping var - _ipp_value_t *value; // Current value - int integer; // Current integer value - cups_lang_t *language; // Current language - char code[32]; // Language code - ipp_tag_t temp_tag; // Temporary value tag + 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; + } + } - // Range check input... - if (!ipp || !attr || !*attr) - return (0); + 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 there is no change, return immediately... - temp_tag = (ipp_tag_t)((int)((*attr)->value_tag) & IPP_TAG_CUPS_MASK); + 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; - if (value_tag == temp_tag) - return (1); + 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; + } - // Otherwise implement changes as needed... - switch (value_tag) - { - case IPP_TAG_UNSUPPORTED_VALUE : - case IPP_TAG_DEFAULT : - case IPP_TAG_UNKNOWN : - case IPP_TAG_NOVALUE : - case IPP_TAG_NOTSETTABLE : - case IPP_TAG_DELETEATTR : - case IPP_TAG_ADMINDEFINE : - // Free any existing values... - if ((*attr)->num_values > 0) - ipp_free_values(*attr, 0, (*attr)->num_values); + 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); + } - // Set out-of-band value... - (*attr)->value_tag = value_tag; + 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_RANGE : - if (temp_tag != IPP_TAG_INTEGER) - return (0); + 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)); - for (i = (*attr)->num_values, value = (*attr)->values; i > 0; i --, value ++) - { - integer = value->integer; - value->range.lower = value->range.upper = integer; - } + 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); + } - (*attr)->value_tag = IPP_TAG_RANGE; + 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_NAME : - if (temp_tag != IPP_TAG_KEYWORD) - return (0); - - (*attr)->value_tag = (ipp_tag_t)(IPP_TAG_NAME | ((*attr)->value_tag & IPP_TAG_CUPS_CONST)); - 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; + } + } - case IPP_TAG_NAMELANG : - case IPP_TAG_TEXTLANG : - if (value_tag == IPP_TAG_NAMELANG && (temp_tag != IPP_TAG_NAME && temp_tag != IPP_TAG_KEYWORD)) - return (0); + 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 (value_tag == IPP_TAG_TEXTLANG && temp_tag != IPP_TAG_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; - if (ipp->attrs && ipp->attrs->next && ipp->attrs->next->name && !strcmp(ipp->attrs->next->name, "attributes-natural-language") && (ipp->attrs->next->value_tag & IPP_TAG_CUPS_MASK) == IPP_TAG_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))); - } + case IPP_TAG_CHARSET : + for (i = 0; i < attr->num_values; i ++) + { + for (ptr = attr->values[i].string.text; *ptr; ptr ++) + { + if (!isprint(*ptr & 255) || isupper(*ptr & 255) || isspace(*ptr & 255)) + break; + } - for (i = (*attr)->num_values - 1, value = (*attr)->values + 1; i > 0; i --, value ++) - value->string.language = (*attr)->values[0].string.language; + if (*ptr || ptr == attr->values[i].string.text) + { + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad charset value \"%s\" - bad characters (RFC 8011 section 5.1.8)."), attr->name, attr->values[i].string.text); + return (0); + } - if ((int)(*attr)->value_tag & IPP_TAG_CUPS_CONST) + if ((ptr - attr->values[i].string.text) > (IPP_MAX_CHARSET - 1)) + { + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad charset value \"%s\" - bad length %d (RFC 8011 section 5.1.8)."), attr->name, attr->values[i].string.text, (int)(ptr - attr->values[i].string.text)); + return (0); + } + } + break; + + case IPP_TAG_LANGUAGE : + // The following regular expression is derived from the ABNF for + // language tags in RFC 4646. All I can say is that this is the + // easiest way to check the values... + if ((r = regcomp(&re, + "^(" + "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})" + // language + "(-[a-z][a-z][a-z][a-z]){0,1}" // script + "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" // region + "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" // variant + "(-[a-wy-z](-[a-z0-9]{2,8})+)*" // extension + "(-x(-[a-z0-9]{1,8})+)*" // privateuse + "|" + "x(-[a-z0-9]{1,8})+" // privateuse + "|" + "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" // grandfathered + ")$", + REG_NOSUB | REG_EXTENDED)) != 0) { - // Make copies of all values... - for (i = (*attr)->num_values, value = (*attr)->values; i > 0; i --, value ++) - value->string.text = _cupsStrAlloc(value->string.text); + char temp[256]; // Temporary error string + + regerror(r, &re, temp, sizeof(temp)); + ipp_set_error(IPP_STATUS_ERROR_INTERNAL, _("Unable to compile naturalLanguage regular expression: %s."), temp); + return (0); } - (*attr)->value_tag = IPP_TAG_NAMELANG; - break; + for (i = 0; i < attr->num_values; i ++) + { + if (regexec(&re, attr->values[i].string.text, 0, NULL, 0)) + { + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad naturalLanguage value \"%s\" - bad characters (RFC 8011 section 5.1.9)."), attr->name, attr->values[i].string.text); + regfree(&re); + return (0); + } - case IPP_TAG_KEYWORD : - if (temp_tag == IPP_TAG_NAME || temp_tag == IPP_TAG_NAMELANG) - break; // Silently "allow" name -> keyword + if (strlen(attr->values[i].string.text) > (IPP_MAX_LANGUAGE - 1)) + { + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad naturalLanguage value \"%s\" - bad length %d (RFC 8011 section 5.1.9)."), attr->name, attr->values[i].string.text, (int)strlen(attr->values[i].string.text)); + regfree(&re); + return (0); + } + } - return (0); + regfree(&re); + break; - case IPP_TAG_EXTENSION : - if (temp_tag == IPP_TAG_STRING && value_tag == IPP_TAG_EXTENSION) + case IPP_TAG_MIMETYPE : + // The following regular expression is derived from the ABNF for + // MIME media types in RFC 2045 and 4288. All I can say is that this is + // the easiest way to check the values... + if ((r = regcomp(&re, + "^" + "[-a-zA-Z0-9!#$&.+^_]{1,127}" // type-name + "/" + "[-a-zA-Z0-9!#$&.+^_]{1,127}" // subtype-name + "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" // parameter= + "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*" + // value + "$", + REG_NOSUB | REG_EXTENDED)) != 0) { - // Allow octetString -> extension - (*attr)->value_tag = value_tag; - break; + char temp[256]; // Temporary error string + + regerror(r, &re, temp, sizeof(temp)); + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("Unable to compile mimeMediaType regular expression: %s."), temp); + return (0); } + for (i = 0; i < attr->num_values; i ++) + { + if (regexec(&re, attr->values[i].string.text, 0, NULL, 0)) + { + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad mimeMediaType value \"%s\" - bad characters (RFC 8011 section 5.1.10)."), attr->name, attr->values[i].string.text); + regfree(&re); + return (0); + } + + if (strlen(attr->values[i].string.text) > (IPP_MAX_MIMETYPE - 1)) + { + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad mimeMediaType value \"%s\" - bad length %d (RFC 8011 section 5.1.10)."), attr->name, attr->values[i].string.text, (int)strlen(attr->values[i].string.text)); + regfree(&re); + return (0); + } + } + + regfree(&re); + break; + default : - return (0); + break; } return (1); @@ -4120,843 +4104,631 @@ ippSetValueTag( // -// 'ippSetVersion()' - Set the version number in an IPP message. -// -// The "ipp" parameter refers to an IPP message previously created using -// the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. +// 'ippValidateAttributes()' - Validate all attributes in an IPP message. // -// The valid version numbers are currently 1.0, 1.1, 2.0, 2.1, and 2.2. +// This function validates the contents of the IPP message, including each +// attribute. Like @link ippValidateAttribute@, @link cupsGetErrorString@ is +// set to a human-readable message on failure. // -// @since CUPS 1.6/macOS 10.8@ +// @since CUPS 1.7/macOS 10.9@ // -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) +int // O - 1 if valid, 0 otherwise +ippValidateAttributes(ipp_t *ipp) // I - IPP message { - // Range check input... - if (!ipp || major < 0 || minor < 0) - return (0); + ipp_attribute_t *attr; // Current attribute - // Set the version number... - ipp->request.any.version[0] = (ipp_uchar_t)major; - ipp->request.any.version[1] = (ipp_uchar_t)minor; + + if (!ipp) + return (1); + + for (attr = ipp->attrs; attr; attr = attr->next) + { + if (!ippValidateAttribute(attr)) + return (0); + } return (1); } // -// 'ippTimeToDate()' - Convert from time in seconds to RFC 2579 format. +// 'ippWrite()' - Write data for an IPP message to a HTTP connection. // -const ipp_uchar_t * // O - RFC-2579 date/time data -ippTimeToDate(time_t t) // I - Time in seconds +ipp_state_t // O - Current state +ippWrite(http_t *http, // I - HTTP connection + ipp_t *ipp) // I - IPP data { - 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) - gmtime_r(&t, &unixdate); - unixdate.tm_year += 1900; + DEBUG_printf("ippWrite(http=%p, ipp=%p)", (void *)http, (void *)ipp); - 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; + if (!http) + return (IPP_STATE_ERROR); - return (date); + return (ippWriteIO(http, (ipp_iocb_t)httpWrite2, http->blocking, NULL, ipp)); } // -// '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 cupsGetErrorString@ is set to a human-readable message. +// 'ippWriteFile()' - Write data for an IPP message to a file. // -// @since CUPS 1.7/macOS 10.9@ +// @since CUPS 1.1.19/macOS 10.3@ // -int // O - 1 if valid, 0 otherwise -ippValidateAttribute( - ipp_attribute_t *attr) // I - Attribute +ipp_state_t // O - Current state +ippWriteFile(int fd, // I - HTTP data + ipp_t *ipp) // I - IPP data { - int i, // Looping var - r; // regcomp() error code - 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 + DEBUG_printf("ippWriteFile(fd=%d, ipp=%p)", fd, (void *)ipp); + ipp->state = IPP_STATE_IDLE; - // Skip separators. - if (!attr->name) - return (1); + return (ippWriteIO(&fd, (ipp_iocb_t)ipp_write_file, 1, NULL, ipp)); +} - // 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); - } +// +// 'ippWriteIO()' - Write data for an IPP message. +// +// @since CUPS 1.2/macOS 10.5@ +// - 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); +ipp_state_t // O - Current state +ippWriteIO(void *dst, // I - Destination + ipp_io_cb_t cb, // I - Write callback function + int blocking, // I - Use blocking IO? + ipp_t *parent, // I - Parent IPP message + ipp_t *ipp) // I - IPP data +{ + int i; // Looping var + int n; // Length of data + unsigned char *buffer, // Data buffer + *bufptr; // Pointer into buffer + ipp_attribute_t *attr; // Current attribute + _ipp_value_t *value; // Current value + + + DEBUG_printf("ippWriteIO(dst=%p, cb=%p, blocking=%d, parent=%p, ipp=%p)", (void *)dst, (void *)cb, blocking, (void *)parent, (void *)ipp); + + if (!dst || !ipp) + return (IPP_STATE_ERROR); + + if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL) + { + DEBUG_puts("1ippWriteIO: Unable to get write buffer"); + return (IPP_STATE_ERROR); } - switch (attr->value_tag) + switch (ipp->state) { - case IPP_TAG_INTEGER : - break; + case IPP_STATE_IDLE : + ipp->state ++; // Avoid common problem... - case IPP_TAG_BOOLEAN : - for (i = 0; i < attr->num_values; i ++) + case IPP_STATE_HEADER : + if (parent == NULL) { - 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; + // Send the request header: + // + // Version = 2 bytes + // Operation/Status Code = 2 bytes + // Request ID = 4 bytes + // Total = 8 bytes + bufptr = buffer; - 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; + *bufptr++ = ipp->request.any.version[0]; + *bufptr++ = ipp->request.any.version[1]; + *bufptr++ = (ipp_uchar_t)(ipp->request.any.op_status >> 8); + *bufptr++ = (ipp_uchar_t)ipp->request.any.op_status; + *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 24); + *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 16); + *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 8); + *bufptr++ = (ipp_uchar_t)ipp->request.any.request_id; - case IPP_TAG_STRING : - for (i = 0; i < attr->num_values; i ++) - { - if (attr->values[i].unknown.length > IPP_MAX_OCTETSTRING) + DEBUG_printf("2ippWriteIO: version=%d.%d", buffer[0], buffer[1]); + DEBUG_printf("2ippWriteIO: op_status=%04x", ipp->request.any.op_status); + DEBUG_printf("2ippWriteIO: request_id=%d", ipp->request.any.request_id); + + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) { - 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); + DEBUG_puts("1ippWriteIO: Could not write IPP header..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); } } - break; - case IPP_TAG_DATE : - for (i = 0; i < attr->num_values; i ++) + // Reset the state engine to point to the first attribute + // in the request/response, with no current group. + ipp->state = IPP_STATE_ATTRIBUTE; + ipp->current = ipp->attrs; + ipp->curtag = IPP_TAG_ZERO; + + DEBUG_printf("1ippWriteIO: ipp->current=%p", (void *)ipp->current); + + // If blocking is disabled, stop here... + if (!blocking) + break; + + case IPP_STATE_ATTRIBUTE : + while (ipp->current != NULL) { - date = attr->values[i].date; + // Write this attribute... + bufptr = buffer; + attr = ipp->current; - 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); - } + ipp->current = ipp->current->next; - if (date[3] < 1 || date[3] > 31) + if (!parent) { - 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 (ipp->curtag != attr->group_tag) + { + // Send a group tag byte... + ipp->curtag = attr->group_tag; - 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 (attr->group_tag == IPP_TAG_ZERO) + continue; - 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); + DEBUG_printf("2ippWriteIO: wrote group tag=%x(%s)", attr->group_tag, ippTagString(attr->group_tag)); + *bufptr++ = (ipp_uchar_t)attr->group_tag; + } + else if (attr->group_tag == IPP_TAG_ZERO) + { + continue; + } } - 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); - } + DEBUG_printf("1ippWriteIO: %s (%s%s)", attr->name, attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag)); - if (date[7] > 9) + // Write the attribute tag and name. + // + // The attribute name length does not include the trailing nul + // character in the source string. + // + // Collection values (parent != NULL) are written differently... + if (parent == NULL) { - 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); - } + // Get the length of the attribute name, and make sure it won't overflow the buffer... + if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 8)) + { + DEBUG_printf("1ippWriteIO: Attribute name too long (%d)", n); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } - 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); - } + // Write the value tag, name length, and name string... + DEBUG_printf("2ippWriteIO: writing value tag=%x(%s)", attr->value_tag, ippTagString(attr->value_tag)); + DEBUG_printf("2ippWriteIO: writing name=%d,\"%s\"", n, attr->name); - if (date[9] > 11) + *bufptr++ = (ipp_uchar_t)attr->value_tag; + *bufptr++ = (ipp_uchar_t)(n >> 8); + *bufptr++ = (ipp_uchar_t)n; + memcpy(bufptr, attr->name, (size_t)n); + bufptr += n; + } + else { - 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); - } + // Get the length of the attribute name, and make sure it won't overflow the buffer... + if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 12)) + { + DEBUG_printf("1ippWriteIO: Attribute name too long (%d)", n); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } - 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; + // Write the member name tag, name length, name string, value tag, + // and empty name for the collection member attribute... + DEBUG_printf("2ippWriteIO: writing value tag=%x(memberName)", IPP_TAG_MEMBERNAME); + DEBUG_printf("2ippWriteIO: writing name=%d,\"%s\"", n, attr->name); + DEBUG_printf("2ippWriteIO: writing value tag=%x(%s)", attr->value_tag, ippTagString(attr->value_tag)); + DEBUG_puts("2ippWriteIO: writing name=0,\"\""); - 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); - } + *bufptr++ = IPP_TAG_MEMBERNAME; + *bufptr++ = 0; + *bufptr++ = 0; + *bufptr++ = (ipp_uchar_t)(n >> 8); + *bufptr++ = (ipp_uchar_t)n; + memcpy(bufptr, attr->name, (size_t)n); + bufptr += n; - 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->value_tag > 0xff) + { + *bufptr++ = IPP_TAG_EXTENSION; + *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 24); + *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 16); + *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 8); + *bufptr++ = (ipp_uchar_t)attr->value_tag; + } + else + { + *bufptr++ = (ipp_uchar_t)attr->value_tag; + } - 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); + *bufptr++ = 0; + *bufptr++ = 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) + // Now write the attribute value(s)... + switch (attr->value_tag & ~IPP_TAG_CUPS_CONST) { - 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; - - case IPP_TAG_CHARSET : - for (i = 0; i < attr->num_values; i ++) - { - for (ptr = attr->values[i].string.text; *ptr; ptr ++) - { - if (!isprint(*ptr & 255) || isupper(*ptr & 255) || isspace(*ptr & 255)) - break; - } - - if (*ptr || ptr == attr->values[i].string.text) - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad charset value \"%s\" - bad characters (RFC 8011 section 5.1.8)."), attr->name, attr->values[i].string.text); - return (0); - } - - if ((ptr - attr->values[i].string.text) > (IPP_MAX_CHARSET - 1)) - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad charset value \"%s\" - bad length %d (RFC 8011 section 5.1.8)."), attr->name, attr->values[i].string.text, (int)(ptr - attr->values[i].string.text)); - return (0); - } - } - break; - - case IPP_TAG_LANGUAGE : - // The following regular expression is derived from the ABNF for - // language tags in RFC 4646. All I can say is that this is the - // easiest way to check the values... - if ((r = regcomp(&re, - "^(" - "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})" - // language - "(-[a-z][a-z][a-z][a-z]){0,1}" // script - "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" // region - "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" // variant - "(-[a-wy-z](-[a-z0-9]{2,8})+)*" // extension - "(-x(-[a-z0-9]{1,8})+)*" // privateuse - "|" - "x(-[a-z0-9]{1,8})+" // privateuse - "|" - "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" // grandfathered - ")$", - REG_NOSUB | REG_EXTENDED)) != 0) - { - char temp[256]; // Temporary error string - - regerror(r, &re, temp, sizeof(temp)); - ipp_set_error(IPP_STATUS_ERROR_INTERNAL, _("Unable to compile naturalLanguage regular expression: %s."), temp); - return (0); - } - - for (i = 0; i < attr->num_values; i ++) - { - if (regexec(&re, attr->values[i].string.text, 0, NULL, 0)) - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad naturalLanguage value \"%s\" - bad characters (RFC 8011 section 5.1.9)."), attr->name, attr->values[i].string.text); - regfree(&re); - return (0); - } - - if (strlen(attr->values[i].string.text) > (IPP_MAX_LANGUAGE - 1)) - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad naturalLanguage value \"%s\" - bad length %d (RFC 8011 section 5.1.9)."), attr->name, attr->values[i].string.text, (int)strlen(attr->values[i].string.text)); - regfree(&re); - return (0); - } - } - - regfree(&re); - break; - - case IPP_TAG_MIMETYPE : - // The following regular expression is derived from the ABNF for - // MIME media types in RFC 2045 and 4288. All I can say is that this is - // the easiest way to check the values... - if ((r = regcomp(&re, - "^" - "[-a-zA-Z0-9!#$&.+^_]{1,127}" // type-name - "/" - "[-a-zA-Z0-9!#$&.+^_]{1,127}" // subtype-name - "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" // parameter= - "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*" - // value - "$", - REG_NOSUB | REG_EXTENDED)) != 0) - { - char temp[256]; // Temporary error string - - regerror(r, &re, temp, sizeof(temp)); - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("Unable to compile mimeMediaType regular expression: %s."), temp); - return (0); - } - - for (i = 0; i < attr->num_values; i ++) - { - if (regexec(&re, attr->values[i].string.text, 0, NULL, 0)) - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad mimeMediaType value \"%s\" - bad characters (RFC 8011 section 5.1.10)."), attr->name, attr->values[i].string.text); - regfree(&re); - return (0); - } - - if (strlen(attr->values[i].string.text) > (IPP_MAX_MIMETYPE - 1)) - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad mimeMediaType value \"%s\" - bad length %d (RFC 8011 section 5.1.10)."), attr->name, attr->values[i].string.text, (int)strlen(attr->values[i].string.text)); - regfree(&re); - return (0); - } - } - - regfree(&re); - break; - - default : - break; - } - - return (1); -} + case IPP_TAG_UNSUPPORTED_VALUE : + case IPP_TAG_DEFAULT : + case IPP_TAG_UNKNOWN : + case IPP_TAG_NOVALUE : + case IPP_TAG_NOTSETTABLE : + case IPP_TAG_DELETEATTR : + case IPP_TAG_ADMINDEFINE : + *bufptr++ = 0; + *bufptr++ = 0; + break; + case IPP_TAG_INTEGER : + case IPP_TAG_ENUM : + for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) + { + if ((IPP_BUF_SIZE - (bufptr - buffer)) < 9) + { + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) + { + DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } -// -// 'ippValidateAttributes()' - Validate all attributes in an IPP message. -// -// This function validates the contents of the IPP message, including each -// attribute. Like @link ippValidateAttribute@, @link cupsGetErrorString@ is -// set to a human-readable message on failure. -// -// @since CUPS 1.7/macOS 10.9@ -// + bufptr = buffer; + } -int // O - 1 if valid, 0 otherwise -ippValidateAttributes(ipp_t *ipp) // I - IPP message -{ - ipp_attribute_t *attr; // Current attribute + if (i) + { + // Arrays and sets are done by sending additional values with a zero-length name... + *bufptr++ = (ipp_uchar_t)attr->value_tag; + *bufptr++ = 0; + *bufptr++ = 0; + } + // Integers and enumerations are both 4-byte signed + // (twos-complement) values. + // + // Put the 2-byte length and 4-byte value into the buffer... + *bufptr++ = 0; + *bufptr++ = 4; + *bufptr++ = (ipp_uchar_t)(value->integer >> 24); + *bufptr++ = (ipp_uchar_t)(value->integer >> 16); + *bufptr++ = (ipp_uchar_t)(value->integer >> 8); + *bufptr++ = (ipp_uchar_t)value->integer; + } + break; - if (!ipp) - return (1); + case IPP_TAG_BOOLEAN : + for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) + { + if ((IPP_BUF_SIZE - (bufptr - buffer)) < 6) + { + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) + { + DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } - for (attr = ipp->attrs; attr; attr = attr->next) - { - if (!ippValidateAttribute(attr)) - return (0); - } + bufptr = buffer; + } - return (1); -} + if (i) + { + // Arrays and sets are done by sending additional values with a zero-length name... + *bufptr++ = (ipp_uchar_t)attr->value_tag; + *bufptr++ = 0; + *bufptr++ = 0; + } + // Boolean values are 1-byte; 0 = false, 1 = true. + // + // Put the 2-byte length and 1-byte value into the buffer... + *bufptr++ = 0; + *bufptr++ = 1; + *bufptr++ = (ipp_uchar_t)value->boolean; + } + break; -// -// 'ippWrite()' - Write data for an IPP message to a HTTP connection. -// + case IPP_TAG_TEXT : + case IPP_TAG_NAME : + case IPP_TAG_KEYWORD : + case IPP_TAG_URI : + case IPP_TAG_URISCHEME : + case IPP_TAG_CHARSET : + case IPP_TAG_LANGUAGE : + case IPP_TAG_MIMETYPE : + for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) + { + if (i) + { + // Arrays and sets are done by sending additional values with a zero-length name... + DEBUG_printf("2ippWriteIO: writing value tag=%x(%s)", attr->value_tag, ippTagString(attr->value_tag)); + DEBUG_printf("2ippWriteIO: writing name=0,\"\""); -ipp_state_t // O - Current state -ippWrite(http_t *http, // I - HTTP connection - ipp_t *ipp) // I - IPP data -{ - DEBUG_printf("ippWrite(http=%p, ipp=%p)", (void *)http, (void *)ipp); + if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3) + { + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) + { + DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } - if (!http) - return (IPP_STATE_ERROR); + bufptr = buffer; + } - return (ippWriteIO(http, (ipp_iocb_t)httpWrite2, http->blocking, NULL, ipp)); -} + *bufptr++ = (ipp_uchar_t)attr->value_tag; + *bufptr++ = 0; + *bufptr++ = 0; + } + if (value->string.text != NULL) + n = (int)strlen(value->string.text); + else + n = 0; -// -// 'ippWriteFile()' - Write data for an IPP message to a file. -// -// @since CUPS 1.1.19/macOS 10.3@ -// + if (n > (IPP_BUF_SIZE - 2)) + { + DEBUG_printf("1ippWriteIO: String too long (%d)", n); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } -ipp_state_t // O - Current state -ippWriteFile(int fd, // I - HTTP data - ipp_t *ipp) // I - IPP data -{ - DEBUG_printf("ippWriteFile(fd=%d, ipp=%p)", fd, (void *)ipp); + DEBUG_printf("2ippWriteIO: writing string=%d,\"%s\"", n, value->string.text); - ipp->state = IPP_STATE_IDLE; + if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2)) + { + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) + { + DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } - return (ippWriteIO(&fd, (ipp_iocb_t)ipp_write_file, 1, NULL, ipp)); -} + bufptr = buffer; + } + // All simple strings consist of the 2-byte length and + // character data without the trailing nul normally found + // in C strings. Also, strings cannot be longer than IPP_MAX_LENGTH + // bytes since the 2-byte length is a signed (twos-complement) + // value. + // + // Put the 2-byte length and string characters in the buffer. + *bufptr++ = (ipp_uchar_t)(n >> 8); + *bufptr++ = (ipp_uchar_t)n; -// -// 'ippWriteIO()' - Write data for an IPP message. -// -// @since CUPS 1.2/macOS 10.5@ -// + if (n > 0) + { + memcpy(bufptr, value->string.text, (size_t)n); + bufptr += n; + } + } + break; -ipp_state_t // O - Current state -ippWriteIO(void *dst, // I - Destination - ipp_io_cb_t cb, // I - Write callback function - int blocking, // I - Use blocking IO? - ipp_t *parent, // I - Parent IPP message - ipp_t *ipp) // I - IPP data -{ - int i; // Looping var - int n; // Length of data - unsigned char *buffer, // Data buffer - *bufptr; // Pointer into buffer - ipp_attribute_t *attr; // Current attribute - _ipp_value_t *value; // Current value + case IPP_TAG_DATE : + for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) + { + if ((IPP_BUF_SIZE - (bufptr - buffer)) < 16) + { + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) + { + DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } + bufptr = buffer; + } - DEBUG_printf("ippWriteIO(dst=%p, cb=%p, blocking=%d, parent=%p, ipp=%p)", (void *)dst, (void *)cb, blocking, (void *)parent, (void *)ipp); + if (i) + { + // Arrays and sets are done by sending additional values with a zero-length name... + *bufptr++ = (ipp_uchar_t)attr->value_tag; + *bufptr++ = 0; + *bufptr++ = 0; + } - if (!dst || !ipp) - return (IPP_STATE_ERROR); + // Date values consist of a 2-byte length and an + // 11-byte date/time structure defined by RFC 1903. + // + // Put the 2-byte length and 11-byte date/time + // structure in the buffer. + *bufptr++ = 0; + *bufptr++ = 11; + memcpy(bufptr, value->date, 11); + bufptr += 11; + } + break; - if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL) - { - DEBUG_puts("1ippWriteIO: Unable to get write buffer"); - return (IPP_STATE_ERROR); - } + case IPP_TAG_RESOLUTION : + for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) + { + if ((IPP_BUF_SIZE - (bufptr - buffer)) < 14) + { + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) + { + DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } - switch (ipp->state) - { - case IPP_STATE_IDLE : - ipp->state ++; // Avoid common problem... + bufptr = buffer; + } - case IPP_STATE_HEADER : - if (parent == NULL) - { - // Send the request header: - // - // Version = 2 bytes - // Operation/Status Code = 2 bytes - // Request ID = 4 bytes - // Total = 8 bytes - bufptr = buffer; + if (i) + { + // Arrays and sets are done by sending additional values with a zero-length name... + *bufptr++ = (ipp_uchar_t)attr->value_tag; + *bufptr++ = 0; + *bufptr++ = 0; + } - *bufptr++ = ipp->request.any.version[0]; - *bufptr++ = ipp->request.any.version[1]; - *bufptr++ = (ipp_uchar_t)(ipp->request.any.op_status >> 8); - *bufptr++ = (ipp_uchar_t)ipp->request.any.op_status; - *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 24); - *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 16); - *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 8); - *bufptr++ = (ipp_uchar_t)ipp->request.any.request_id; + // Resolution values consist of a 2-byte length, + // 4-byte horizontal resolution value, 4-byte vertical + // resolution value, and a 1-byte units value. + // + // Put the 2-byte length and resolution value data + // into the buffer. + *bufptr++ = 0; + *bufptr++ = 9; + *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 24); + *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 16); + *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 8); + *bufptr++ = (ipp_uchar_t)value->resolution.xres; + *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 24); + *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 16); + *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 8); + *bufptr++ = (ipp_uchar_t)value->resolution.yres; + *bufptr++ = (ipp_uchar_t)value->resolution.units; + } + break; - DEBUG_printf("2ippWriteIO: version=%d.%d", buffer[0], buffer[1]); - DEBUG_printf("2ippWriteIO: op_status=%04x", ipp->request.any.op_status); - DEBUG_printf("2ippWriteIO: request_id=%d", ipp->request.any.request_id); + case IPP_TAG_RANGE : + for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) + { + if ((IPP_BUF_SIZE - (bufptr - buffer)) < 13) + { + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) + { + DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP header..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } - } + bufptr = buffer; + } - // Reset the state engine to point to the first attribute - // in the request/response, with no current group. - ipp->state = IPP_STATE_ATTRIBUTE; - ipp->current = ipp->attrs; - ipp->curtag = IPP_TAG_ZERO; + if (i) + { + // Arrays and sets are done by sending additional values with a zero-length name... + *bufptr++ = (ipp_uchar_t)attr->value_tag; + *bufptr++ = 0; + *bufptr++ = 0; + } - DEBUG_printf("1ippWriteIO: ipp->current=%p", (void *)ipp->current); + // Range values consist of a 2-byte length, + // 4-byte lower value, and 4-byte upper value. + // + // Put the 2-byte length and range value data + // into the buffer. + *bufptr++ = 0; + *bufptr++ = 8; + *bufptr++ = (ipp_uchar_t)(value->range.lower >> 24); + *bufptr++ = (ipp_uchar_t)(value->range.lower >> 16); + *bufptr++ = (ipp_uchar_t)(value->range.lower >> 8); + *bufptr++ = (ipp_uchar_t)value->range.lower; + *bufptr++ = (ipp_uchar_t)(value->range.upper >> 24); + *bufptr++ = (ipp_uchar_t)(value->range.upper >> 16); + *bufptr++ = (ipp_uchar_t)(value->range.upper >> 8); + *bufptr++ = (ipp_uchar_t)value->range.upper; + } + break; - // If blocking is disabled, stop here... - if (!blocking) - break; + case IPP_TAG_TEXTLANG : + case IPP_TAG_NAMELANG : + for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) + { + if (i) + { + // Arrays and sets are done by sending additional values with a zero-length name... + if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3) + { + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) + { + DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } - case IPP_STATE_ATTRIBUTE : - while (ipp->current != NULL) - { - // Write this attribute... - bufptr = buffer; - attr = ipp->current; + bufptr = buffer; + } - ipp->current = ipp->current->next; + *bufptr++ = (ipp_uchar_t)attr->value_tag; + *bufptr++ = 0; + *bufptr++ = 0; + } - if (!parent) - { - if (ipp->curtag != attr->group_tag) - { - // Send a group tag byte... - ipp->curtag = attr->group_tag; + // textWithLanguage and nameWithLanguage values consist + // of a 2-byte length for both strings and their + // individual lengths, a 2-byte length for the + // character string, the character string without the + // trailing nul, a 2-byte length for the character + // set string, and the character set string without + // the trailing nul. + n = 4; - if (attr->group_tag == IPP_TAG_ZERO) - continue; + if (value->string.language != NULL) + n += (int)strlen(value->string.language); - DEBUG_printf("2ippWriteIO: wrote group tag=%x(%s)", attr->group_tag, ippTagString(attr->group_tag)); - *bufptr++ = (ipp_uchar_t)attr->group_tag; - } - else if (attr->group_tag == IPP_TAG_ZERO) - { - continue; - } - } + if (value->string.text != NULL) + n += (int)strlen(value->string.text); - DEBUG_printf("1ippWriteIO: %s (%s%s)", attr->name, attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag)); + if (n > (IPP_BUF_SIZE - 2)) + { + DEBUG_printf("1ippWriteIO: text/nameWithLanguage value too long (%d)", n); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } - // Write the attribute tag and name. - // - // The attribute name length does not include the trailing nul - // character in the source string. - // - // Collection values (parent != NULL) are written differently... - if (parent == NULL) - { - // Get the length of the attribute name, and make sure it won't overflow the buffer... - if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 8)) - { - DEBUG_printf("1ippWriteIO: Attribute name too long (%d)", n); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2)) + { + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) + { + DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } - // Write the value tag, name length, and name string... - DEBUG_printf("2ippWriteIO: writing value tag=%x(%s)", attr->value_tag, ippTagString(attr->value_tag)); - DEBUG_printf("2ippWriteIO: writing name=%d,\"%s\"", n, attr->name); + bufptr = buffer; + } - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = (ipp_uchar_t)(n >> 8); - *bufptr++ = (ipp_uchar_t)n; - memcpy(bufptr, attr->name, (size_t)n); - bufptr += n; - } - else - { - // Get the length of the attribute name, and make sure it won't overflow the buffer... - if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 12)) - { - DEBUG_printf("1ippWriteIO: Attribute name too long (%d)", n); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + // Length of entire value + *bufptr++ = (ipp_uchar_t)(n >> 8); + *bufptr++ = (ipp_uchar_t)n; - // Write the member name tag, name length, name string, value tag, - // and empty name for the collection member attribute... - DEBUG_printf("2ippWriteIO: writing value tag=%x(memberName)", IPP_TAG_MEMBERNAME); - DEBUG_printf("2ippWriteIO: writing name=%d,\"%s\"", n, attr->name); - DEBUG_printf("2ippWriteIO: writing value tag=%x(%s)", attr->value_tag, ippTagString(attr->value_tag)); - DEBUG_puts("2ippWriteIO: writing name=0,\"\""); + // Length of language + if (value->string.language != NULL) + n = (int)strlen(value->string.language); + else + n = 0; - *bufptr++ = IPP_TAG_MEMBERNAME; - *bufptr++ = 0; - *bufptr++ = 0; - *bufptr++ = (ipp_uchar_t)(n >> 8); - *bufptr++ = (ipp_uchar_t)n; - memcpy(bufptr, attr->name, (size_t)n); - bufptr += n; + *bufptr++ = (ipp_uchar_t)(n >> 8); + *bufptr++ = (ipp_uchar_t)n; - if (attr->value_tag > 0xff) - { - *bufptr++ = IPP_TAG_EXTENSION; - *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 24); - *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 16); - *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 8); - *bufptr++ = (ipp_uchar_t)attr->value_tag; - } - else - { - *bufptr++ = (ipp_uchar_t)attr->value_tag; - } + // Language + if (n > 0) + { + memcpy(bufptr, value->string.language, (size_t)n); + bufptr += n; + } - *bufptr++ = 0; - *bufptr++ = 0; - } + // Length of text + if (value->string.text != NULL) + n = (int)strlen(value->string.text); + else + n = 0; - // Now write the attribute value(s)... - switch (attr->value_tag & ~IPP_TAG_CUPS_CONST) - { - case IPP_TAG_UNSUPPORTED_VALUE : - case IPP_TAG_DEFAULT : - case IPP_TAG_UNKNOWN : - case IPP_TAG_NOVALUE : - case IPP_TAG_NOTSETTABLE : - case IPP_TAG_DELETEATTR : - case IPP_TAG_ADMINDEFINE : - *bufptr++ = 0; - *bufptr++ = 0; - break; + *bufptr++ = (ipp_uchar_t)(n >> 8); + *bufptr++ = (ipp_uchar_t)n; - case IPP_TAG_INTEGER : - case IPP_TAG_ENUM : + // Text + if (n > 0) + { + memcpy(bufptr, value->string.text, (size_t)n); + bufptr += n; + } + } + break; + + case IPP_TAG_BEGIN_COLLECTION : for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) { - if ((IPP_BUF_SIZE - (bufptr - buffer)) < 9) + // Collections are written with the begin-collection + // tag first with a value of 0 length, followed by the + // attributes in the collection, then the end-collection + // value... + if ((IPP_BUF_SIZE - (bufptr - buffer)) < 5) { if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) { @@ -4976,951 +4748,1211 @@ ippWriteIO(void *dst, // I - Destination *bufptr++ = 0; } - // Integers and enumerations are both 4-byte signed - // (twos-complement) values. - // - // Put the 2-byte length and 4-byte value into the buffer... + // Write a data length of 0 and flush the buffer... *bufptr++ = 0; - *bufptr++ = 4; - *bufptr++ = (ipp_uchar_t)(value->integer >> 24); - *bufptr++ = (ipp_uchar_t)(value->integer >> 16); - *bufptr++ = (ipp_uchar_t)(value->integer >> 8); - *bufptr++ = (ipp_uchar_t)value->integer; + *bufptr++ = 0; + + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) + { + DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } + + bufptr = buffer; + + // Then write the collection attribute... + value->collection->state = IPP_STATE_IDLE; + + if (ippWriteIO(dst, cb, 1, ipp, value->collection) == IPP_STATE_ERROR) + { + DEBUG_puts("1ippWriteIO: Unable to write collection value"); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } + } + break; + + default : + for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) + { + if (i) + { + // Arrays and sets are done by sending additional values with a zero-length name... + if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3) + { + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) + { + DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } + + bufptr = buffer; + } + + *bufptr++ = (ipp_uchar_t)attr->value_tag; + *bufptr++ = 0; + *bufptr++ = 0; + } + + // An unknown value might some new value that a + // vendor has come up with. It consists of a + // 2-byte length and the bytes in the unknown + // value buffer. + n = value->unknown.length; + + if (n > (IPP_BUF_SIZE - 2)) + { + DEBUG_printf("1ippWriteIO: Data length too long (%d)", n); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } + + if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2)) + { + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) + { + DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } + + bufptr = buffer; + } + + // Length of unknown value + *bufptr++ = (ipp_uchar_t)(n >> 8); + *bufptr++ = (ipp_uchar_t)n; + + // Value + if (n > 0) + { + memcpy(bufptr, value->unknown.data, (size_t)n); + bufptr += n; + } } break; + } + + // Write the data out... + if (bufptr > buffer) + { + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) + { + DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } + + DEBUG_printf("2ippWriteIO: wrote %d bytes", (int)(bufptr - buffer)); + } + + // If blocking is disabled and we aren't at the end of the attribute list, stop here... + if (!blocking && ipp->current) + break; + } + + if (ipp->current == NULL) + { + // Done with all of the attributes; add the end-of-attributes tag or end-collection attribute... + if (parent == NULL) + { + buffer[0] = IPP_TAG_END; + n = 1; + } + else + { + buffer[0] = IPP_TAG_END_COLLECTION; + buffer[1] = 0; // empty name + buffer[2] = 0; + buffer[3] = 0; // empty value + buffer[4] = 0; + n = 5; + } + + if ((*cb)(dst, buffer, (size_t)n) < 0) + { + DEBUG_puts("1ippWriteIO: Could not write IPP end-tag..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } + + ipp->state = IPP_STATE_DATA; + } + break; + + case IPP_STATE_DATA : + break; + + default : + break; // anti-compiler-warning-code + } + + _cupsBufferRelease((char *)buffer); + + return (ipp->state); +} + + +// +// 'ipp_add_attr()' - Add a new attribute to the message. +// + +static ipp_attribute_t * // O - New attribute +ipp_add_attr(ipp_t *ipp, // I - IPP message + const char *name, // I - Attribute name or NULL + ipp_tag_t group_tag, // I - Group tag or IPP_TAG_ZERO + ipp_tag_t value_tag, // I - Value tag or IPP_TAG_ZERO + int num_values) // I - Number of values +{ + int alloc_values; // Number of values to allocate + ipp_attribute_t *attr; // New attribute + + + DEBUG_printf("4ipp_add_attr(ipp=%p, name=\"%s\", group_tag=0x%x, value_tag=0x%x, num_values=%d)", (void *)ipp, name, group_tag, value_tag, num_values); + + // Range check input... + if (!ipp || num_values < 0) + return (NULL); + + // Allocate memory, rounding the allocation up as needed... + if (num_values <= 1) + alloc_values = 1; + else + alloc_values = (num_values + IPP_MAX_VALUES - 1) & ~(IPP_MAX_VALUES - 1); + + attr = calloc(1, sizeof(ipp_attribute_t) + + (size_t)(alloc_values - 1) * sizeof(_ipp_value_t)); + + if (attr) + { + // Initialize attribute... + DEBUG_printf("4debug_alloc: %p %s %s%s (%d values)", (void *)attr, name, num_values > 1 ? "1setOf " : "", ippTagString(value_tag), num_values); - case IPP_TAG_BOOLEAN : - for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) - { - if ((IPP_BUF_SIZE - (bufptr - buffer)) < 6) - { - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + if (name) + attr->name = _cupsStrAlloc(name); - bufptr = buffer; - } + attr->group_tag = group_tag; + attr->value_tag = value_tag; + attr->num_values = num_values; - if (i) - { - // Arrays and sets are done by sending additional values with a zero-length name... - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } + // Add it to the end of the linked list... + if (ipp->last) + ipp->last->next = attr; + else + ipp->attrs = attr; - // Boolean values are 1-byte; 0 = false, 1 = true. - // - // Put the 2-byte length and 1-byte value into the buffer... - *bufptr++ = 0; - *bufptr++ = 1; - *bufptr++ = (ipp_uchar_t)value->boolean; - } - break; + ipp->prev = ipp->last; + ipp->last = ipp->current = attr; + } - case IPP_TAG_TEXT : - case IPP_TAG_NAME : - case IPP_TAG_KEYWORD : - case IPP_TAG_URI : - case IPP_TAG_URISCHEME : - case IPP_TAG_CHARSET : - case IPP_TAG_LANGUAGE : - case IPP_TAG_MIMETYPE : - for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) - { - if (i) - { - // Arrays and sets are done by sending additional values with a zero-length name... - DEBUG_printf("2ippWriteIO: writing value tag=%x(%s)", attr->value_tag, ippTagString(attr->value_tag)); - DEBUG_printf("2ippWriteIO: writing name=0,\"\""); + DEBUG_printf("5ipp_add_attr: Returning %p", (void *)attr); - if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3) - { - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + return (attr); +} - bufptr = buffer; - } - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } +// +// 'ipp_free_values()' - Free attribute values. +// - if (value->string.text != NULL) - n = (int)strlen(value->string.text); - else - n = 0; +static void +ipp_free_values(ipp_attribute_t *attr, // I - Attribute to free values from + int element,// I - First value to free + int count) // I - Number of values to free +{ + int i; // Looping var + _ipp_value_t *value; // Current value - if (n > (IPP_BUF_SIZE - 2)) - { - DEBUG_printf("1ippWriteIO: String too long (%d)", n); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } - DEBUG_printf("2ippWriteIO: writing string=%d,\"%s\"", n, value->string.text); + DEBUG_printf("4ipp_free_values(attr=%p, element=%d, count=%d)", (void *)attr, element, count); - if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2)) - { - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + if (!(attr->value_tag & IPP_TAG_CUPS_CONST)) + { + // Free values as needed... + switch (attr->value_tag) + { + case IPP_TAG_TEXTLANG : + case IPP_TAG_NAMELANG : + if (element == 0 && count == attr->num_values && attr->values[0].string.language) + { + _cupsStrFree(attr->values[0].string.language); + attr->values[0].string.language = NULL; + } + // Fall through to other string values - bufptr = buffer; - } + case IPP_TAG_TEXT : + case IPP_TAG_NAME : + case IPP_TAG_RESERVED_STRING : + case IPP_TAG_KEYWORD : + case IPP_TAG_URI : + case IPP_TAG_URISCHEME : + case IPP_TAG_CHARSET : + case IPP_TAG_LANGUAGE : + case IPP_TAG_MIMETYPE : + for (i = count, value = attr->values + element; i > 0; i --, value ++) + { + _cupsStrFree(value->string.text); + value->string.text = NULL; + } + break; - // All simple strings consist of the 2-byte length and - // character data without the trailing nul normally found - // in C strings. Also, strings cannot be longer than IPP_MAX_LENGTH - // bytes since the 2-byte length is a signed (twos-complement) - // value. - // - // Put the 2-byte length and string characters in the buffer. - *bufptr++ = (ipp_uchar_t)(n >> 8); - *bufptr++ = (ipp_uchar_t)n; + case IPP_TAG_UNSUPPORTED_VALUE : + case IPP_TAG_DEFAULT : + case IPP_TAG_UNKNOWN : + case IPP_TAG_NOVALUE : + case IPP_TAG_NOTSETTABLE : + case IPP_TAG_DELETEATTR : + case IPP_TAG_ADMINDEFINE : + case IPP_TAG_INTEGER : + case IPP_TAG_ENUM : + case IPP_TAG_BOOLEAN : + case IPP_TAG_DATE : + case IPP_TAG_RESOLUTION : + case IPP_TAG_RANGE : + break; - if (n > 0) - { - memcpy(bufptr, value->string.text, (size_t)n); - bufptr += n; - } - } - break; + case IPP_TAG_BEGIN_COLLECTION : + for (i = count, value = attr->values + element; i > 0; i --, value ++) + { + ippDelete(value->collection); + value->collection = NULL; + } + break; - case IPP_TAG_DATE : - for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) - { - if ((IPP_BUF_SIZE - (bufptr - buffer)) < 16) - { - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + case IPP_TAG_STRING : + default : + for (i = count, value = attr->values + element; i > 0; i --, value ++) + { + if (value->unknown.data) + { + free(value->unknown.data); + value->unknown.data = NULL; + } + } + break; + } + } - bufptr = buffer; - } + // If we are not freeing values from the end, move the remaining values up... + if ((element + count) < attr->num_values) + memmove(attr->values + element, attr->values + element + count, (size_t)(attr->num_values - count - element) * sizeof(_ipp_value_t)); - if (i) - { - // Arrays and sets are done by sending additional values with a zero-length name... - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } + attr->num_values -= count; +} - // Date values consist of a 2-byte length and an - // 11-byte date/time structure defined by RFC 1903. - // - // Put the 2-byte length and 11-byte date/time - // structure in the buffer. - *bufptr++ = 0; - *bufptr++ = 11; - memcpy(bufptr, value->date, 11); - bufptr += 11; - } - break; - case IPP_TAG_RESOLUTION : - for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) - { - if ((IPP_BUF_SIZE - (bufptr - buffer)) < 14) - { - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } +// +// 'ipp_get_code()' - Convert a C locale/charset name into an IPP language/charset code. +// +// This typically converts strings of the form "ll_CC", "ll-REGION", and "CHARSET_NUMBER" +// to "ll-cc", "ll-region", and "charset-number", respectively. +// - bufptr = buffer; - } +static char * // O - Language code string +ipp_get_code(const char *value, // I - Locale/charset string + char *buffer, // I - String buffer + size_t bufsize) // I - Size of string buffer +{ + char *bufptr, // Pointer into buffer + *bufend; // End of buffer - if (i) - { - // Arrays and sets are done by sending additional values with a zero-length name... - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } - // Resolution values consist of a 2-byte length, - // 4-byte horizontal resolution value, 4-byte vertical - // resolution value, and a 1-byte units value. - // - // Put the 2-byte length and resolution value data - // into the buffer. - *bufptr++ = 0; - *bufptr++ = 9; - *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 24); - *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 16); - *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 8); - *bufptr++ = (ipp_uchar_t)value->resolution.xres; - *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 24); - *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 16); - *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 8); - *bufptr++ = (ipp_uchar_t)value->resolution.yres; - *bufptr++ = (ipp_uchar_t)value->resolution.units; - } - break; + // Convert values to lowercase and change _ to - as needed... + for (bufptr = buffer, bufend = buffer + bufsize - 1; *value && bufptr < bufend; value ++) + { + if (*value == '_') + *bufptr++ = '-'; + else + *bufptr++ = (char)_cups_tolower(*value); + } + *bufptr = '\0'; + + // Return the converted string... + return (buffer); +} - case IPP_TAG_RANGE : - for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) - { - if ((IPP_BUF_SIZE - (bufptr - buffer)) < 13) - { - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } - bufptr = buffer; - } +// +// 'ipp_lang_code()' - Convert a C locale name into an IPP language code. +// +// This typically converts strings of the form "ll_CC" and "ll-REGION" to "ll-cc" and +// "ll-region", respectively. It also converts the "C" (POSIX) locale to "en". +// - if (i) - { - // Arrays and sets are done by sending additional values with a zero-length name... - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } +static char * // O - Language code string +ipp_lang_code(const char *locale, // I - Locale string + char *buffer, // I - String buffer + size_t bufsize) // I - Size of string buffer +{ + // Map POSIX ("C") locale to generic English, otherwise convert the locale string as-is. + if (!_cups_strcasecmp(locale, "c")) + { + cupsCopyString(buffer, "en", bufsize); + return (buffer); + } + else + { + return (ipp_get_code(locale, buffer, bufsize)); + } +} - // Range values consist of a 2-byte length, - // 4-byte lower value, and 4-byte upper value. - // - // Put the 2-byte length and range value data - // into the buffer. - *bufptr++ = 0; - *bufptr++ = 8; - *bufptr++ = (ipp_uchar_t)(value->range.lower >> 24); - *bufptr++ = (ipp_uchar_t)(value->range.lower >> 16); - *bufptr++ = (ipp_uchar_t)(value->range.lower >> 8); - *bufptr++ = (ipp_uchar_t)value->range.lower; - *bufptr++ = (ipp_uchar_t)(value->range.upper >> 24); - *bufptr++ = (ipp_uchar_t)(value->range.upper >> 16); - *bufptr++ = (ipp_uchar_t)(value->range.upper >> 8); - *bufptr++ = (ipp_uchar_t)value->range.upper; - } - break; - case IPP_TAG_TEXTLANG : - case IPP_TAG_NAMELANG : - for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) - { - if (i) - { - // Arrays and sets are done by sending additional values with a zero-length name... - if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3) - { - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } +// +// 'ipp_length()' - Compute the length of an IPP message or collection value. +// - bufptr = buffer; - } +static size_t // O - Size of IPP message +ipp_length(ipp_t *ipp, // I - IPP message or collection + int collection) // I - 1 if a collection, 0 otherwise +{ + int i; // Looping var + size_t bytes; // Number of bytes + ipp_attribute_t *attr; // Current attribute + ipp_tag_t group; // Current group + _ipp_value_t *value; // Current value - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } - // textWithLanguage and nameWithLanguage values consist - // of a 2-byte length for both strings and their - // individual lengths, a 2-byte length for the - // character string, the character string without the - // trailing nul, a 2-byte length for the character - // set string, and the character set string without - // the trailing nul. - n = 4; + DEBUG_printf("3ipp_length(ipp=%p, collection=%d)", (void *)ipp, collection); - if (value->string.language != NULL) - n += (int)strlen(value->string.language); + if (!ipp) + { + DEBUG_puts("4ipp_length: Returning 0 bytes"); + return (0); + } - if (value->string.text != NULL) - n += (int)strlen(value->string.text); + // Start with 8 bytes for the IPP message header... + bytes = collection ? 0 : 8; - if (n > (IPP_BUF_SIZE - 2)) - { - DEBUG_printf("1ippWriteIO: text/nameWithLanguage value too long (%d)", n); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + // Then add the lengths of each attribute... + group = IPP_TAG_ZERO; - if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2)) - { - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + for (attr = ipp->attrs; attr != NULL; attr = attr->next) + { + if (attr->group_tag != group && !collection) + { + group = attr->group_tag; + if (group == IPP_TAG_ZERO) + continue; - bufptr = buffer; - } + bytes ++; // Group tag + } - // Length of entire value - *bufptr++ = (ipp_uchar_t)(n >> 8); - *bufptr++ = (ipp_uchar_t)n; + if (!attr->name) + continue; - // Length of language - if (value->string.language != NULL) - n = (int)strlen(value->string.language); - else - n = 0; + DEBUG_printf("5ipp_length: attr->name=\"%s\", attr->num_values=%d, bytes=" CUPS_LLFMT, attr->name, attr->num_values, CUPS_LLCAST bytes); - *bufptr++ = (ipp_uchar_t)(n >> 8); - *bufptr++ = (ipp_uchar_t)n; + if ((attr->value_tag & ~IPP_TAG_CUPS_CONST) < IPP_TAG_EXTENSION) + bytes += (size_t)attr->num_values;// Value tag for each value + else + bytes += (size_t)(5 * attr->num_values); + // Value tag for each value + bytes += (size_t)(2 * attr->num_values); + // Name lengths + bytes += strlen(attr->name); // Name + bytes += (size_t)(2 * attr->num_values); + // Value lengths - // Language - if (n > 0) - { - memcpy(bufptr, value->string.language, (size_t)n); - bufptr += n; - } + if (collection) + bytes += 5; // Add membername overhead - // Length of text - if (value->string.text != NULL) - n = (int)strlen(value->string.text); - else - n = 0; + switch (attr->value_tag & ~IPP_TAG_CUPS_CONST) + { + case IPP_TAG_UNSUPPORTED_VALUE : + case IPP_TAG_DEFAULT : + case IPP_TAG_UNKNOWN : + case IPP_TAG_NOVALUE : + case IPP_TAG_NOTSETTABLE : + case IPP_TAG_DELETEATTR : + case IPP_TAG_ADMINDEFINE : + break; - *bufptr++ = (ipp_uchar_t)(n >> 8); - *bufptr++ = (ipp_uchar_t)n; + case IPP_TAG_INTEGER : + case IPP_TAG_ENUM : + bytes += (size_t)(4 * attr->num_values); + break; - // Text - if (n > 0) - { - memcpy(bufptr, value->string.text, (size_t)n); - bufptr += n; - } - } - break; + case IPP_TAG_BOOLEAN : + bytes += (size_t)attr->num_values; + break; - case IPP_TAG_BEGIN_COLLECTION : - for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) - { - // Collections are written with the begin-collection - // tag first with a value of 0 length, followed by the - // attributes in the collection, then the end-collection - // value... - if ((IPP_BUF_SIZE - (bufptr - buffer)) < 5) - { - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + case IPP_TAG_TEXT : + case IPP_TAG_NAME : + case IPP_TAG_KEYWORD : + case IPP_TAG_URI : + case IPP_TAG_URISCHEME : + case IPP_TAG_CHARSET : + case IPP_TAG_LANGUAGE : + case IPP_TAG_MIMETYPE : + for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) + { + if (value->string.text) + bytes += strlen(value->string.text); + } + break; - bufptr = buffer; - } + case IPP_TAG_DATE : + bytes += (size_t)(11 * attr->num_values); + break; - if (i) - { - // Arrays and sets are done by sending additional values with a zero-length name... - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } + case IPP_TAG_RESOLUTION : + bytes += (size_t)(9 * attr->num_values); + break; - // Write a data length of 0 and flush the buffer... - *bufptr++ = 0; - *bufptr++ = 0; + case IPP_TAG_RANGE : + bytes += (size_t)(8 * attr->num_values); + break; - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + case IPP_TAG_TEXTLANG : + case IPP_TAG_NAMELANG : + bytes += (size_t)(4 * attr->num_values); + // Charset + text length - bufptr = buffer; + for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) + { + if (value->string.language) + bytes += strlen(value->string.language); - // Then write the collection attribute... - value->collection->state = IPP_STATE_IDLE; + if (value->string.text) + bytes += strlen(value->string.text); + } + break; - if (ippWriteIO(dst, cb, 1, ipp, value->collection) == IPP_STATE_ERROR) - { - DEBUG_puts("1ippWriteIO: Unable to write collection value"); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } - } - break; + case IPP_TAG_BEGIN_COLLECTION : + for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) + bytes += ipp_length(value->collection, 1); + break; - default : - for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) - { - if (i) - { - // Arrays and sets are done by sending additional values with a zero-length name... - if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3) - { - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + default : + for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) + bytes += (size_t)value->unknown.length; + break; + } + } - bufptr = buffer; - } + // Finally, add 1 byte for the "end of attributes" tag or 5 bytes for the "end of collection" tag and return... + if (collection) + bytes += 5; + else + bytes ++; - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } + DEBUG_printf("4ipp_length: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST bytes); - // An unknown value might some new value that a - // vendor has come up with. It consists of a - // 2-byte length and the bytes in the unknown - // value buffer. - n = value->unknown.length; + return (bytes); +} - if (n > (IPP_BUF_SIZE - 2)) - { - DEBUG_printf("1ippWriteIO: Data length too long (%d)", n); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } - if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2)) - { - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } +// +// 'ipp_read_file()' - Read IPP data from a file. +// - bufptr = buffer; - } +static ssize_t // O - Number of bytes read +ipp_read_file(int *fd, // I - File descriptor + ipp_uchar_t *buffer, // O - Read buffer + size_t length) // I - Number of bytes to read +{ +#ifdef _WIN32 + return ((ssize_t)read(*fd, buffer, (unsigned)length)); +#else + return (read(*fd, buffer, length)); +#endif // _WIN32 +} - // Length of unknown value - *bufptr++ = (ipp_uchar_t)(n >> 8); - *bufptr++ = (ipp_uchar_t)n; - // Value - if (n > 0) - { - memcpy(bufptr, value->unknown.data, (size_t)n); - bufptr += n; - } - } - break; - } +// +// 'ipp_read_http()' - Semi-blocking read on a HTTP connection... +// - // Write the data out... - if (bufptr > buffer) - { - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP attribute..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } +static ssize_t // O - Number of bytes read +ipp_read_http(http_t *http, // I - Client connection + ipp_uchar_t *buffer, // O - Buffer for data + size_t length) // I - Total length +{ + ssize_t tbytes, // Total bytes read + bytes; // Bytes read this pass - DEBUG_printf("2ippWriteIO: wrote %d bytes", (int)(bufptr - buffer)); - } - // If blocking is disabled and we aren't at the end of the attribute list, stop here... - if (!blocking && ipp->current) - break; - } + DEBUG_printf("7ipp_read_http(http=%p, buffer=%p, length=%d)", (void *)http, (void *)buffer, (int)length); - if (ipp->current == NULL) - { - // Done with all of the attributes; add the end-of-attributes tag or end-collection attribute... - if (parent == NULL) - { - buffer[0] = IPP_TAG_END; - n = 1; - } - else - { - buffer[0] = IPP_TAG_END_COLLECTION; - buffer[1] = 0; // empty name - buffer[2] = 0; - buffer[3] = 0; // empty value - buffer[4] = 0; - n = 5; - } + // Loop until all bytes are read... + for (tbytes = 0, bytes = 0; tbytes < (int)length; tbytes += bytes, buffer += bytes) + { + DEBUG_printf("9ipp_read_http: tbytes=" CUPS_LLFMT ", http->state=%d", CUPS_LLCAST tbytes, http->state); - if ((*cb)(dst, buffer, (size_t)n) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP end-tag..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + if (http->state == HTTP_STATE_WAITING) + break; - ipp->state = IPP_STATE_DATA; - } - break; + if (http->used == 0 && !http->blocking) + { + // Wait up to 10 seconds for more data on non-blocking sockets... + if (!httpWait(http, 10000)) + { + // Signal no data... + bytes = -1; + break; + } + } + else if (http->used == 0 && http->timeout_value > 0) + { + // Wait up to timeout seconds for more data on blocking sockets... + if (!httpWait(http, (int)(1000 * http->timeout_value))) + { + // Signal no data... + bytes = -1; + break; + } + } - case IPP_STATE_DATA : - break; + if ((bytes = httpRead2(http, (char *)buffer, length - (size_t)tbytes)) < 0) + { +#ifdef _WIN32 + break; +#else + if (errno != EAGAIN && errno != EINTR) + break; - default : - break; // anti-compiler-warning-code + bytes = 0; +#endif // _WIN32 + } + else if (bytes == 0) + { + break; + } } - _cupsBufferRelease((char *)buffer); + // Return the number of bytes read... + if (tbytes == 0 && bytes < 0) + tbytes = -1; - return (ipp->state); -} + DEBUG_printf("8ipp_read_http: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST tbytes); + return (tbytes); +} -// -// 'ipp_add_attr()' - Add a new attribute to the message. -// -static ipp_attribute_t * // O - New attribute -ipp_add_attr(ipp_t *ipp, // I - IPP message - const char *name, // I - Attribute name or NULL - ipp_tag_t group_tag, // I - Group tag or IPP_TAG_ZERO - ipp_tag_t value_tag, // I - Value tag or IPP_TAG_ZERO - int num_values) // I - Number of values -{ - int alloc_values; // Number of values to allocate - ipp_attribute_t *attr; // New attribute +// +// 'ipp_read_io()' - Read data for an IPP message. +// +static ipp_state_t // O - Current state +ipp_read_io(void *src, // I - Data source + ipp_io_cb_t cb, // I - Read callback function + int blocking, // I - Use blocking IO? + ipp_t *parent, // I - Parent request, if any + ipp_t *ipp, // I - IPP data + int depth) // I - Depth of collection +{ + int n; // Length of data + unsigned char *buffer, // Data buffer + string[IPP_MAX_TEXT], + // Small string buffer + *bufptr, // Pointer into buffer + *bufend; // End of buffer + ipp_attribute_t *attr = NULL; // Current attribute + ipp_tag_t tag; // Current tag + ipp_tag_t value_tag; // Current value tag + _ipp_value_t *value; // Current value - DEBUG_printf("4ipp_add_attr(ipp=%p, name=\"%s\", group_tag=0x%x, value_tag=0x%x, num_values=%d)", (void *)ipp, name, group_tag, value_tag, num_values); - // Range check input... - if (!ipp || num_values < 0) - return (NULL); + DEBUG_printf("ipp_read_io(src=%p, cb=%p, blocking=%d, parent=%p, ipp=%p, depth=%d)", (void *)src, (void *)cb, blocking, (void *)parent, (void *)ipp, depth); + DEBUG_printf("2ipp_read_io: ipp->state=%d", ipp->state); - // Allocate memory, rounding the allocation up as needed... - if (num_values <= 1) - alloc_values = 1; - else - alloc_values = (num_values + IPP_MAX_VALUES - 1) & ~(IPP_MAX_VALUES - 1); + if (depth > 10) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP message nested too deeply."), 1); + return (IPP_STATE_ERROR); + } - attr = calloc(1, sizeof(ipp_attribute_t) + - (size_t)(alloc_values - 1) * sizeof(_ipp_value_t)); + if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL) + { + DEBUG_puts("1ipp_read_io: Unable to get read buffer."); + return (IPP_STATE_ERROR); + } - if (attr) + switch (ipp->state) { - // Initialize attribute... - DEBUG_printf("4debug_alloc: %p %s %s%s (%d values)", (void *)attr, name, num_values > 1 ? "1setOf " : "", ippTagString(value_tag), num_values); + case IPP_STATE_IDLE : + ipp->state ++; // Avoid common problem... - if (name) - attr->name = _cupsStrAlloc(name); + case IPP_STATE_HEADER : + if (parent == NULL) + { + // Get the request header... + if ((*cb)(src, buffer, 8) < 8) + { + DEBUG_puts("1ipp_read_io: Unable to read header."); + goto rollback; + } - attr->group_tag = group_tag; - attr->value_tag = value_tag; - attr->num_values = num_values; + // Then copy the request header over... + ipp->request.any.version[0] = buffer[0]; + ipp->request.any.version[1] = buffer[1]; + ipp->request.any.op_status = (buffer[2] << 8) | buffer[3]; + ipp->request.any.request_id = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; - // Add it to the end of the linked list... - if (ipp->last) - ipp->last->next = attr; - else - ipp->attrs = attr; + DEBUG_printf("2ipp_read_io: version=%d.%d", buffer[0], buffer[1]); + DEBUG_printf("2ipp_read_io: op_status=%04x", ipp->request.any.op_status); + DEBUG_printf("2ipp_read_io: request_id=%d", ipp->request.any.request_id); + } - ipp->prev = ipp->last; - ipp->last = ipp->current = attr; - } + ipp->state = IPP_STATE_ATTRIBUTE; + ipp->current = NULL; + ipp->curtag = IPP_TAG_ZERO; + ipp->prev = ipp->last; - DEBUG_printf("5ipp_add_attr: Returning %p", (void *)attr); + // If blocking is disabled, stop here... + if (!blocking) + break; - return (attr); -} + case IPP_STATE_ATTRIBUTE : + for (;;) + { + if ((*cb)(src, buffer, 1) < 1) + { + DEBUG_puts("1ipp_read_io: Callback returned EOF/error"); + goto rollback; + } + DEBUG_printf("2ipp_read_io: ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev); -// -// 'ipp_free_values()' - Free attribute values. -// + // Read this attribute... + tag = (ipp_tag_t)buffer[0]; -static void -ipp_free_values(ipp_attribute_t *attr, // I - Attribute to free values from - int element,// I - First value to free - int count) // I - Number of values to free -{ - int i; // Looping var - _ipp_value_t *value; // Current value + if (tag == IPP_TAG_END) + { + // No more attributes left... + DEBUG_puts("2ipp_read_io: IPP_TAG_END."); + ipp->state = IPP_STATE_DATA; + break; + } + else if (tag == IPP_TAG_ZERO || (tag == IPP_TAG_OPERATION && ipp->curtag != IPP_TAG_ZERO)) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid group tag."), 1); + DEBUG_printf("1ipp_read_io: bad tag 0x%02x.", tag); + goto rollback; + } + else if (tag < IPP_TAG_UNSUPPORTED_VALUE) + { + // Group tag... Set the current group and continue... + if (parent) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid group tag."), 1); + DEBUG_printf("1ipp_read_io: bad tag 0x%02x.", tag); + goto rollback; + } + else if (ipp->curtag == tag) + { + ipp->prev = ippAddSeparator(ipp); + } + else if (ipp->current) + { + ipp->prev = ipp->current; + } - DEBUG_printf("4ipp_free_values(attr=%p, element=%d, count=%d)", (void *)attr, element, count); + ipp->curtag = tag; + ipp->current = NULL; + attr = NULL; + DEBUG_printf("2ipp_read_io: group tag=%x(%s), ipp->prev=%p", tag, ippTagString(tag), (void *)ipp->prev); + continue; + } - if (!(attr->value_tag & IPP_TAG_CUPS_CONST)) - { - // Free values as needed... - switch (attr->value_tag) - { - case IPP_TAG_TEXTLANG : - case IPP_TAG_NAMELANG : - if (element == 0 && count == attr->num_values && attr->values[0].string.language) + DEBUG_printf("2ipp_read_io: value tag=%x(%s)", tag, ippTagString(tag)); + + // Get the name... + if ((*cb)(src, buffer, 2) < 2) { - _cupsStrFree(attr->values[0].string.language); - attr->values[0].string.language = NULL; + DEBUG_puts("1ipp_read_io: unable to read name length."); + goto rollback; } - // Fall through to other string values - case IPP_TAG_TEXT : - case IPP_TAG_NAME : - case IPP_TAG_RESERVED_STRING : - case IPP_TAG_KEYWORD : - case IPP_TAG_URI : - case IPP_TAG_URISCHEME : - case IPP_TAG_CHARSET : - case IPP_TAG_LANGUAGE : - case IPP_TAG_MIMETYPE : - for (i = count, value = attr->values + element; i > 0; i --, value ++) + n = (buffer[0] << 8) | buffer[1]; + + if (n >= IPP_BUF_SIZE) { - _cupsStrFree(value->string.text); - value->string.text = NULL; + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP name larger than 32767 bytes."), 1); + DEBUG_printf("1ipp_read_io: bad name length %d.", n); + goto rollback; } - break; - case IPP_TAG_UNSUPPORTED_VALUE : - case IPP_TAG_DEFAULT : - case IPP_TAG_UNKNOWN : - case IPP_TAG_NOVALUE : - case IPP_TAG_NOTSETTABLE : - case IPP_TAG_DELETEATTR : - case IPP_TAG_ADMINDEFINE : - case IPP_TAG_INTEGER : - case IPP_TAG_ENUM : - case IPP_TAG_BOOLEAN : - case IPP_TAG_DATE : - case IPP_TAG_RESOLUTION : - case IPP_TAG_RANGE : - break; + DEBUG_printf("2ipp_read_io: name length=%d", n); - case IPP_TAG_BEGIN_COLLECTION : - for (i = count, value = attr->values + element; i > 0; i --, value ++) + if (n && parent) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid named IPP attribute in collection."), 1); + DEBUG_puts("1ipp_read_io: bad attribute name in collection."); + goto rollback; + } + else if (n == 0 && tag != IPP_TAG_MEMBERNAME && tag != IPP_TAG_END_COLLECTION) { - ippDelete(value->collection); - value->collection = NULL; + // More values for current attribute... + if (ipp->current == NULL) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP attribute has no name."), 1); + DEBUG_puts("1ipp_read_io: Attribute without name and no current."); + goto rollback; + } + + attr = ipp->current; + value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_CUPS_MASK); + + // Make sure we aren't adding a new value of a different type... + if (value_tag == IPP_TAG_ZERO) + { + // Setting the value of a collection member... + attr->value_tag = tag; + } + else if (value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_NAMELANG || (value_tag >= IPP_TAG_TEXT && value_tag <= IPP_TAG_MIMETYPE)) + { + // String values can sometimes come across in different forms; accept sets of differing values... + if (tag != IPP_TAG_TEXTLANG && tag != IPP_TAG_NAMELANG && (tag < IPP_TAG_TEXT || tag > IPP_TAG_MIMETYPE) && tag != IPP_TAG_NOVALUE) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP 1setOf attribute with incompatible value tags."), 1); + DEBUG_printf("1ipp_read_io: 1setOf value tag %x(%s) != %x(%s)", value_tag, ippTagString(value_tag), tag, ippTagString(tag)); + goto rollback; + } + + if (value_tag != tag) + { + DEBUG_printf("1ipp_read_io: Converting %s attribute from %s to %s.", attr->name, ippTagString(value_tag), ippTagString(tag)); + if (!ippSetValueTag(ipp, &attr, tag)) + goto rollback; + } + } + else if (value_tag == IPP_TAG_INTEGER || value_tag == IPP_TAG_RANGE) + { + // Integer and rangeOfInteger values can sometimes be mixed; accept sets of differing values... + if (tag != IPP_TAG_INTEGER && tag != IPP_TAG_RANGE) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP 1setOf attribute with incompatible value tags."), 1); + DEBUG_printf("1ipp_read_io: 1setOf value tag %x(%s) != %x(%s)", value_tag, ippTagString(value_tag), tag, ippTagString(tag)); + goto rollback; + } + + if (value_tag == IPP_TAG_INTEGER && tag == IPP_TAG_RANGE) + { + // Convert integer values to rangeOfInteger values... + DEBUG_printf("1ipp_read_io: Converting %s attribute to rangeOfInteger.", attr->name); + ippSetValueTag(ipp, &attr, IPP_TAG_RANGE); + } + } + else if (value_tag != tag) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP 1setOf attribute with incompatible value tags."), 1); + DEBUG_printf("1ipp_read_io: value tag %x(%s) != %x(%s)", value_tag, ippTagString(value_tag), tag, ippTagString(tag)); + goto rollback; + } + + // Finally, reallocate the attribute array as needed... + if ((value = ipp_set_value(ipp, &attr, attr->num_values)) == NULL) + goto rollback; } - break; + else if (tag == IPP_TAG_MEMBERNAME) + { + // Name must be length 0! + if (n) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP member name is not empty."), 1); + DEBUG_puts("1ipp_read_io: member name not empty."); + goto rollback; + } + else if (!parent) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP member attribute outside of collection."), 1); + DEBUG_puts("1ipp_read_io: member attribute outside of collection."); + goto rollback; + } + + if (ipp->current) + ipp->prev = ipp->current; + + attr = ipp->current = ipp_add_attr(ipp, NULL, ipp->curtag, IPP_TAG_ZERO, 1); + if (!attr) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), false); + DEBUG_puts("1ipp_read_io: unable to allocate attribute."); + goto rollback; + } - case IPP_TAG_STRING : - default : - for (i = count, value = attr->values + element; i > 0; i --, value ++) + DEBUG_printf("2ipp_read_io: membername, ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev); + + value = attr->values; + } + else if (tag != IPP_TAG_END_COLLECTION) { - if (value->unknown.data) + // New attribute; read the name and add it... + if ((*cb)(src, buffer, (size_t)n) < n) { - free(value->unknown.data); - value->unknown.data = NULL; + DEBUG_puts("1ipp_read_io: unable to read name."); + goto rollback; } - } - break; - } - } - // If we are not freeing values from the end, move the remaining values up... - if ((element + count) < attr->num_values) - memmove(attr->values + element, attr->values + element + count, (size_t)(attr->num_values - count - element) * sizeof(_ipp_value_t)); + buffer[n] = '\0'; - attr->num_values -= count; -} + if (ipp->current) + ipp->prev = ipp->current; + if ((attr = ipp->current = ipp_add_attr(ipp, (char *)buffer, ipp->curtag, tag, 1)) == NULL) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), false); + DEBUG_puts("1ipp_read_io: unable to allocate attribute."); + goto rollback; + } -// -// 'ipp_get_code()' - Convert a C locale/charset name into an IPP language/charset code. -// -// This typically converts strings of the form "ll_CC", "ll-REGION", and "CHARSET_NUMBER" -// to "ll-cc", "ll-region", and "charset-number", respectively. -// + DEBUG_printf("2ipp_read_io: name=\"%s\", ipp->current=%p, ipp->prev=%p", buffer, (void *)ipp->current, (void *)ipp->prev); -static char * // O - Language code string -ipp_get_code(const char *value, // I - Locale/charset string - char *buffer, // I - String buffer - size_t bufsize) // I - Size of string buffer -{ - char *bufptr, // Pointer into buffer - *bufend; // End of buffer + value = attr->values; + } + else + { + attr = NULL; + value = NULL; + } + if ((*cb)(src, buffer, 2) < 2) + { + DEBUG_puts("1ipp_read_io: unable to read value length."); + goto rollback; + } - // Convert values to lowercase and change _ to - as needed... - for (bufptr = buffer, bufend = buffer + bufsize - 1; *value && bufptr < bufend; value ++) - { - if (*value == '_') - *bufptr++ = '-'; - else - *bufptr++ = (char)_cups_tolower(*value); - } - *bufptr = '\0'; + n = (buffer[0] << 8) | buffer[1]; + DEBUG_printf("2ipp_read_io: value length=%d", n); - // Return the converted string... - return (buffer); -} + if (n >= IPP_BUF_SIZE) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, + _("IPP value larger than 32767 bytes."), 1); + DEBUG_printf("1ipp_read_io: bad value length %d.", n); + goto rollback; + } + switch (tag) + { + case IPP_TAG_INTEGER : + case IPP_TAG_ENUM : + if (n != 4) + { + if (tag == IPP_TAG_INTEGER) + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP integer value not 4 bytes."), 1); + else + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP enum value not 4 bytes."), 1); + DEBUG_printf("1ipp_read_io: bad integer value length %d.", n); + goto rollback; + } -// -// 'ipp_lang_code()' - Convert a C locale name into an IPP language code. -// -// This typically converts strings of the form "ll_CC" and "ll-REGION" to "ll-cc" and -// "ll-region", respectively. It also converts the "C" (POSIX) locale to "en". -// + if ((*cb)(src, buffer, 4) < 4) + { + DEBUG_puts("1ipp_read_io: Unable to read integer value."); + goto rollback; + } -static char * // O - Language code string -ipp_lang_code(const char *locale, // I - Locale string - char *buffer, // I - String buffer - size_t bufsize) // I - Size of string buffer -{ - // Map POSIX ("C") locale to generic English, otherwise convert the locale string as-is. - if (!_cups_strcasecmp(locale, "c")) - { - cupsCopyString(buffer, "en", bufsize); - return (buffer); - } - else - { - return (ipp_get_code(locale, buffer, bufsize)); - } -} + n = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; + if (attr->value_tag == IPP_TAG_RANGE) + value->range.lower = value->range.upper = n; + else + value->integer = n; + break; -// -// 'ipp_length()' - Compute the length of an IPP message or collection value. -// + case IPP_TAG_BOOLEAN : + if (n != 1) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP boolean value not 1 byte."), + 1); + DEBUG_printf("1ipp_read_io: bad boolean value length %d.", n); + goto rollback; + } -static size_t // O - Size of IPP message -ipp_length(ipp_t *ipp, // I - IPP message or collection - int collection) // I - 1 if a collection, 0 otherwise -{ - int i; // Looping var - size_t bytes; // Number of bytes - ipp_attribute_t *attr; // Current attribute - ipp_tag_t group; // Current group - _ipp_value_t *value; // Current value + if ((*cb)(src, buffer, 1) < 1) + { + DEBUG_puts("1ipp_read_io: Unable to read boolean value."); + goto rollback; + } + value->boolean = (char)buffer[0]; + break; - DEBUG_printf("3ipp_length(ipp=%p, collection=%d)", (void *)ipp, collection); + case IPP_TAG_UNSUPPORTED_VALUE : + case IPP_TAG_DEFAULT : + case IPP_TAG_UNKNOWN : + case IPP_TAG_NOVALUE : + case IPP_TAG_NOTSETTABLE : + case IPP_TAG_DELETEATTR : + case IPP_TAG_ADMINDEFINE : + // These value types are not supposed to have values, however + // some vendors (Brother) do not implement IPP correctly and so + // we need to map non-empty values to text... + if (attr->value_tag == tag) + { + if (n == 0) + break; - if (!ipp) - { - DEBUG_puts("4ipp_length: Returning 0 bytes"); - return (0); - } + attr->value_tag = IPP_TAG_TEXT; + } - // Start with 8 bytes for the IPP message header... - bytes = collection ? 0 : 8; + case IPP_TAG_TEXT : + case IPP_TAG_NAME : + case IPP_TAG_RESERVED_STRING : + case IPP_TAG_KEYWORD : + case IPP_TAG_URI : + case IPP_TAG_URISCHEME : + case IPP_TAG_CHARSET : + case IPP_TAG_LANGUAGE : + case IPP_TAG_MIMETYPE : + if (n > 0) + { + if ((*cb)(src, buffer, (size_t)n) < n) + { + DEBUG_puts("1ipp_read_io: unable to read string value."); + goto rollback; + } + } - // Then add the lengths of each attribute... - group = IPP_TAG_ZERO; + buffer[n] = '\0'; + value->string.text = _cupsStrAlloc((char *)buffer); + DEBUG_printf("2ipp_read_io: value=\"%s\"(%p)", value->string.text, value->string.text); + break; - for (attr = ipp->attrs; attr != NULL; attr = attr->next) - { - if (attr->group_tag != group && !collection) - { - group = attr->group_tag; - if (group == IPP_TAG_ZERO) - continue; + case IPP_TAG_DATE : + if (n != 11) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP date value not 11 bytes."), 1); + DEBUG_printf("1ipp_read_io: bad date value length %d.", n); + goto rollback; + } - bytes ++; // Group tag - } + if ((*cb)(src, value->date, 11) < 11) + { + DEBUG_puts("1ipp_read_io: Unable to read date value."); + goto rollback; + } + break; - if (!attr->name) - continue; + case IPP_TAG_RESOLUTION : + if (n != 9) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP resolution value not 9 bytes."), 1); + DEBUG_printf("1ipp_read_io: bad resolution value length %d.", n); + goto rollback; + } - DEBUG_printf("5ipp_length: attr->name=\"%s\", attr->num_values=%d, bytes=" CUPS_LLFMT, attr->name, attr->num_values, CUPS_LLCAST bytes); + if ((*cb)(src, buffer, 9) < 9) + { + DEBUG_puts("1ipp_read_io: Unable to read resolution value."); + goto rollback; + } - if ((attr->value_tag & ~IPP_TAG_CUPS_CONST) < IPP_TAG_EXTENSION) - bytes += (size_t)attr->num_values;// Value tag for each value - else - bytes += (size_t)(5 * attr->num_values); - // Value tag for each value - bytes += (size_t)(2 * attr->num_values); - // Name lengths - bytes += strlen(attr->name); // Name - bytes += (size_t)(2 * attr->num_values); - // Value lengths + value->resolution.xres = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; + value->resolution.yres = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; + value->resolution.units = + (ipp_res_t)buffer[8]; + break; - if (collection) - bytes += 5; // Add membername overhead + case IPP_TAG_RANGE : + if (n != 8) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP rangeOfInteger value not 8 bytes."), 1); + DEBUG_printf("1ipp_read_io: bad rangeOfInteger value length %d.", n); + goto rollback; + } + + if ((*cb)(src, buffer, 8) < 8) + { + DEBUG_puts("1ipp_read_io: Unable to read range value."); + goto rollback; + } + + value->range.lower = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; + value->range.upper = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; + break; + + case IPP_TAG_TEXTLANG : + case IPP_TAG_NAMELANG : + if (n < 4) + { + if (tag == IPP_TAG_TEXTLANG) + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP textWithLanguage value less than minimum 4 bytes."), true); + else + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP nameWithLanguage value less than minimum 4 bytes."), true); + DEBUG_printf("1ipp_read_io: bad stringWithLanguage value length %d.", n); + goto rollback; + } - switch (attr->value_tag & ~IPP_TAG_CUPS_CONST) - { - case IPP_TAG_UNSUPPORTED_VALUE : - case IPP_TAG_DEFAULT : - case IPP_TAG_UNKNOWN : - case IPP_TAG_NOVALUE : - case IPP_TAG_NOTSETTABLE : - case IPP_TAG_DELETEATTR : - case IPP_TAG_ADMINDEFINE : - break; + if ((*cb)(src, buffer, (size_t)n) < n) + { + DEBUG_puts("1ipp_read_io: Unable to read string w/language value."); + goto rollback; + } - case IPP_TAG_INTEGER : - case IPP_TAG_ENUM : - bytes += (size_t)(4 * attr->num_values); - break; + bufptr = buffer; + bufend = buffer + n; - case IPP_TAG_BOOLEAN : - bytes += (size_t)attr->num_values; - break; + // textWithLanguage and nameWithLanguage are composite values: + // + // language-length + // language + // text-length + // text + n = (bufptr[0] << 8) | bufptr[1]; - case IPP_TAG_TEXT : - case IPP_TAG_NAME : - case IPP_TAG_KEYWORD : - case IPP_TAG_URI : - case IPP_TAG_URISCHEME : - case IPP_TAG_CHARSET : - case IPP_TAG_LANGUAGE : - case IPP_TAG_MIMETYPE : - for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) - { - if (value->string.text) - bytes += strlen(value->string.text); - } - break; + if ((bufptr + 2 + n + 2) > bufend || n >= (int)sizeof(string)) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP language length overflows value."), 1); + DEBUG_printf("1ipp_read_io: bad language value length %d.", n); + goto rollback; + } + else if (n >= IPP_MAX_LANGUAGE) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP language length too large."), 1); + DEBUG_printf("1ipp_read_io: bad language value length %d.", n); + goto rollback; + } - case IPP_TAG_DATE : - bytes += (size_t)(11 * attr->num_values); - break; + memcpy(string, bufptr + 2, (size_t)n); + string[n] = '\0'; - case IPP_TAG_RESOLUTION : - bytes += (size_t)(9 * attr->num_values); - break; + value->string.language = _cupsStrAlloc((char *)string); - case IPP_TAG_RANGE : - bytes += (size_t)(8 * attr->num_values); - break; + bufptr += 2 + n; + n = (bufptr[0] << 8) | bufptr[1]; - case IPP_TAG_TEXTLANG : - case IPP_TAG_NAMELANG : - bytes += (size_t)(4 * attr->num_values); - // Charset + text length + if ((bufptr + 2 + n) > bufend) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP string length overflows value."), 1); + DEBUG_printf("1ipp_read_io: bad string value length %d.", n); + goto rollback; + } - for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) - { - if (value->string.language) - bytes += strlen(value->string.language); + bufptr[2 + n] = '\0'; + value->string.text = _cupsStrAlloc((char *)bufptr + 2); + break; - if (value->string.text) - bytes += strlen(value->string.text); - } - break; + case IPP_TAG_BEGIN_COLLECTION : + // Oh boy, here comes a collection value, so read it... + value->collection = ippNew(); - case IPP_TAG_BEGIN_COLLECTION : - for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) - bytes += ipp_length(value->collection, 1); - break; + if (n > 0) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP begCollection value not 0 bytes."), 1); + DEBUG_puts("1ipp_read_io: begCollection tag with value length > 0."); + goto rollback; + } - default : - for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) - bytes += (size_t)value->unknown.length; - break; - } - } + if (ipp_read_io(src, cb, 1, ipp, value->collection, depth + 1) == IPP_STATE_ERROR) + { + DEBUG_puts("1ipp_read_io: Unable to read collection value."); + goto rollback; + } + break; - // Finally, add 1 byte for the "end of attributes" tag or 5 bytes for the "end of collection" tag and return... - if (collection) - bytes += 5; - else - bytes ++; + case IPP_TAG_END_COLLECTION : + if (n > 0) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP endCollection value not 0 bytes."), 1); + DEBUG_puts("1ipp_read_io: endCollection tag with value length > 0."); + goto rollback; + } - DEBUG_printf("4ipp_length: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST bytes); + _cupsBufferRelease((char *)buffer); - return (bytes); -} + DEBUG_puts("1ipp_read_io: endCollection tag..."); + return (ipp->state = IPP_STATE_DATA); + + case IPP_TAG_MEMBERNAME : + // The value the name of the member in the collection, which + // we need to carry over... + if (!attr) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP memberName with no attribute."), 1); + DEBUG_puts("1ipp_read_io: Member name without attribute."); + goto rollback; + } + else if (n == 0) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP memberName value is empty."), 1); + DEBUG_puts("1ipp_read_io: Empty member name value."); + goto rollback; + } + else if ((*cb)(src, buffer, (size_t)n) < n) + { + DEBUG_puts("1ipp_read_io: Unable to read member name value."); + goto rollback; + } + buffer[n] = '\0'; + attr->name = _cupsStrAlloc((char *)buffer); -// -// 'ipp_read_http()' - Semi-blocking read on a HTTP connection... -// + // Since collection members are encoded differently than + // regular attributes, make sure we don't start with an + // empty value... + attr->num_values --; -static ssize_t // O - Number of bytes read -ipp_read_http(http_t *http, // I - Client connection - ipp_uchar_t *buffer, // O - Buffer for data - size_t length) // I - Total length -{ - ssize_t tbytes, // Total bytes read - bytes; // Bytes read this pass + DEBUG_printf("2ipp_read_io: member name=\"%s\"", attr->name); + break; + case IPP_TAG_STRING : + default : // Other unsupported values + if (tag == IPP_TAG_STRING && n > IPP_MAX_LENGTH) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP octetString length too large."), 1); + DEBUG_printf("1ipp_read_io: bad octetString value length %d.", n); + goto rollback; + } - DEBUG_printf("7ipp_read_http(http=%p, buffer=%p, length=%d)", (void *)http, (void *)buffer, (int)length); + value->unknown.length = (size_t)n; - // Loop until all bytes are read... - for (tbytes = 0, bytes = 0; tbytes < (int)length; tbytes += bytes, buffer += bytes) - { - DEBUG_printf("9ipp_read_http: tbytes=" CUPS_LLFMT ", http->state=%d", CUPS_LLCAST tbytes, http->state); + if (n > 0) + { + if ((value->unknown.data = malloc((size_t)n)) == NULL) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), false); + DEBUG_puts("1ipp_read_io: Unable to allocate value"); + goto rollback; + } - if (http->state == HTTP_STATE_WAITING) - break; + if ((*cb)(src, value->unknown.data, (size_t)n) < n) + { + DEBUG_puts("1ipp_read_io: Unable to read unsupported value."); + goto rollback; + } + } + else + { + value->unknown.data = NULL; + } + break; + } - if (http->used == 0 && !http->blocking) - { - // Wait up to 10 seconds for more data on non-blocking sockets... - if (!httpWait(http, 10000)) - { - // Signal no data... - bytes = -1; - break; - } - } - else if (http->used == 0 && http->timeout_value > 0) - { - // Wait up to timeout seconds for more data on blocking sockets... - if (!httpWait(http, (int)(1000 * http->timeout_value))) - { - // Signal no data... - bytes = -1; - break; - } - } + // If blocking is disabled, stop here... + if (!blocking) + break; + } + break; - if ((bytes = httpRead2(http, (char *)buffer, length - (size_t)tbytes)) < 0) - { -#ifdef _WIN32 - break; -#else - if (errno != EAGAIN && errno != EINTR) - break; + case IPP_STATE_DATA : + break; - bytes = 0; -#endif // _WIN32 - } - else if (bytes == 0) - { - break; - } + default : + break; // anti-compiler-warning-code } - // Return the number of bytes read... - if (tbytes == 0 && bytes < 0) - tbytes = -1; + DEBUG_printf("1ipp_read_io: returning ipp->state=%d.", ipp->state); + _cupsBufferRelease((char *)buffer); - DEBUG_printf("8ipp_read_http: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST tbytes); + return (ipp->state); - return (tbytes); -} + // If we get here, there was an error that required us to roll back the last + // attribute read in order to keep the IPP message valid... + rollback: + _cupsBufferRelease((char *)buffer); -// -// 'ipp_read_file()' - Read IPP data from a file. -// + if (attr) + ippDeleteAttribute(ipp, attr); -static ssize_t // O - Number of bytes read -ipp_read_file(int *fd, // I - File descriptor - ipp_uchar_t *buffer, // O - Read buffer - size_t length) // I - Number of bytes to read -{ -#ifdef _WIN32 - return ((ssize_t)read(*fd, buffer, (unsigned)length)); -#else - return (read(*fd, buffer, length)); -#endif // _WIN32 + return (IPP_STATE_ERROR); }