diff --git a/CHANGES.md b/CHANGES.md index 3ceaadeb5..b554e5253 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,7 @@ Changes in CUPS v2.4.12 (YYYY-MM-DD) ------------------------------------ - Fixed the default User-Agent string. +- Fixed a recursion issue in `ippReadIO`. Changes in CUPS v2.4.11 (2024-09-30) diff --git a/cups/ipp.c b/cups/ipp.c index 47ba9fa26..874f62295 100644 --- a/cups/ipp.c +++ b/cups/ipp.c @@ -33,10 +33,9 @@ static void ipp_free_values(ipp_attribute_t *attr, int element, 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_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_iocb_t cb, bool 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, @@ -2824,13 +2823,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_iocb_t)ipp_read_http, http->blocking, NULL, - ipp)); + return (ipp_read_io(http, (ipp_iocb_t)ipp_read_http, http->blocking, /*parent*/NULL, ipp, /*depth*/0)); } @@ -2846,7 +2844,10 @@ ippReadFile(int fd, /* I - HTTP data */ { DEBUG_printf(("ippReadFile(fd=%d, ipp=%p)", fd, (void *)ipp)); - return (ippReadIO(&fd, (ipp_iocb_t)ipp_read_file, 1, NULL, ipp)); + if (!ipp) + return (IPP_STATE_ERROR); + + return (ipp_read_io(&fd, (ipp_iocb_t)ipp_read_file, /*blocking*/1, /*parent*/NULL, ipp, /*depth*/0)); } @@ -2863,753 +2864,397 @@ 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) - { - DEBUG_puts("1ippReadIO: Unable to get read buffer."); - return (IPP_STATE_ERROR); - } - - switch (ipp->state) - { - case IPP_STATE_IDLE : - ipp->state ++; /* Avoid common problem... */ - - case IPP_STATE_HEADER : - if (parent == NULL) - { - /* - * Get the request header... - */ + return (ipp_read_io(src, cb, blocking, parent, ipp, /*depth*/0)); +} - if ((*cb)(src, buffer, 8) < 8) - { - DEBUG_puts("1ippReadIO: Unable to read header."); - goto rollback; - } - /* - * Then copy the request header over... - */ +/* + * 'ippSetBoolean()' - Set a boolean value in an attribute. + * + * The @code ipp@ parameter refers to an IPP message previously created using + * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. + * + * The @code attr@ parameter may be modified as a result of setting the value. + * + * The @code element@ parameter specifies which value to set from 0 to + * @code ippGetCount(attr)@. + * + * @since CUPS 1.6/macOS 10.8@ + */ - 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]; +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 */ - 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)); - } - ipp->state = IPP_STATE_ATTRIBUTE; - ipp->current = NULL; - ipp->curtag = IPP_TAG_ZERO; - ipp->prev = ipp->last; + /* + * Range check input... + */ - /* - * If blocking is disabled, stop here... - */ + if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BOOLEAN || + element < 0 || element > (*attr)->num_values) + return (0); - if (!blocking) - break; + /* + * Set the value and return... + */ - case IPP_STATE_ATTRIBUTE : - for (;;) - { - if ((*cb)(src, buffer, 1) < 1) - { - DEBUG_puts("1ippReadIO: Callback returned EOF/error"); - goto rollback; - } + 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_EXTENSION) - { - /* - * Read 32-bit "extension" tag... - */ +/* + * 'ippSetCollection()' - Set a collection value in an attribute. + * + * The @code ipp@ parameter refers to an IPP message previously created using + * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. + * + * The @code attr@ parameter may be modified as a result of setting the value. + * + * The @code element@ parameter specifies which value to set from 0 to + * @code ippGetCount(attr)@. + * + * @since CUPS 1.6/macOS 10.8@ + */ - if ((*cb)(src, buffer, 4) < 4) - { - DEBUG_puts("1ippReadIO: Callback returned EOF/error"); - goto rollback; - } +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 */ - tag = (ipp_tag_t)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); - if (tag & IPP_TAG_CUPS_CONST) - { - /* - * Fail if the high bit is set in the tag... - */ + /* + * Range check input... + */ - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP extension tag larger than 0x7FFFFFFF."), 1); - DEBUG_printf(("1ippReadIO: bad tag 0x%x.", tag)); - goto rollback; - } - } + if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BEGIN_COLLECTION || + element < 0 || element > (*attr)->num_values || !colvalue) + return (0); - if (tag == IPP_TAG_END) - { - /* - * No more attributes left... - */ + /* + * Set the value and return... + */ - DEBUG_puts("2ippReadIO: IPP_TAG_END."); + if ((value = ipp_set_value(ipp, attr, element)) != NULL) + { + if (value->collection) + ippDelete(value->collection); - 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... - */ + value->collection = colvalue; + colvalue->use ++; + } - 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; + return (value != NULL); +} - 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))); +/* + * 'ippSetDate()' - Set a dateTime value in an attribute. + * + * The @code ipp@ parameter refers to an IPP message previously created using + * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. + * + * The @code attr@ parameter may be modified as a result of setting the value. + * + * The @code element@ parameter specifies which value to set from 0 to + * @code ippGetCount(attr)@. + * + * @since CUPS 1.6/macOS 10.8@ + */ - /* - * Get the name... - */ +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 */ - if ((*cb)(src, buffer, 2) < 2) - { - DEBUG_puts("1ippReadIO: unable to read name length."); - goto rollback; - } - n = (buffer[0] << 8) | buffer[1]; + /* + * Range check input... + */ - 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; - } + 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); - DEBUG_printf(("2ippReadIO: name length=%d", n)); + /* + * Set the value and return... + */ - 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 ((value = ipp_set_value(ipp, attr, element)) != NULL) + memcpy(value->date, datevalue, sizeof(value->date)); - 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; - } + return (value != NULL); +} - 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... - */ +/* + * 'ippSetGroupTag()' - Set the group tag of an attribute. + * + * The @code ipp@ parameter refers to an IPP message previously created using + * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. + * + * The @code attr@ parameter may be modified as a result of setting the value. + * + * The @code group@ parameter specifies the IPP attribute group tag: none + * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@), + * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation + * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription + * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@). + * + * @since CUPS 1.6/macOS 10.8@ + */ - 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... - */ +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 (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 (!ipp || !attr || !*attr || + group_tag < IPP_TAG_ZERO || group_tag == IPP_TAG_END || + group_tag >= IPP_TAG_UNSUPPORTED_VALUE) + return (0); - 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... - */ + /* + * Set the group tag and return... + */ - 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; - } + (*attr)->group_tag = group_tag; - if (value_tag == IPP_TAG_INTEGER && tag == IPP_TAG_RANGE) - { - /* - * Convert integer values to rangeOfInteger values... - */ + return (1); +} - 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... - */ +/* + * 'ippSetInteger()' - Set an integer or enum value in an attribute. + * + * The @code ipp@ parameter refers to an IPP message previously created using + * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. + * + * The @code attr@ parameter may be modified as a result of setting the value. + * + * The @code element@ parameter specifies which value to set from 0 to + * @code ippGetCount(attr)@. + * + * @since CUPS 1.6/macOS 10.8@ + */ - if ((value = ipp_set_value(ipp, &attr, attr->num_values)) == NULL) - goto rollback; - } - else if (tag == IPP_TAG_MEMBERNAME) - { - /* - * Name must be length 0! - */ +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 */ - 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; + /* + * Range check input... + */ - 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; - } + 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); - DEBUG_printf(("2ippReadIO: membername, ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev)); + /* + * Set the value and return... + */ - value = attr->values; - } - else if (tag != IPP_TAG_END_COLLECTION) - { - /* - * New attribute; read the name and add it... - */ + 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, (size_t)n) < n) - { - DEBUG_puts("1ippReadIO: unable to read name."); - goto rollback; - } + value->integer = intvalue; + } - buffer[n] = '\0'; + return (value != NULL); +} - 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, _("Unable to allocate IPP attribute."), 1); - DEBUG_puts("1ippReadIO: unable to allocate attribute."); - goto rollback; - } +/* + * 'ippSetName()' - Set the name of an attribute. + * + * The @code ipp@ parameter refers to an IPP message previously created using + * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. + * + * The @code attr@ parameter may be modified as a result of setting the value. + * + * @since CUPS 1.6/macOS 10.8@ + */ - DEBUG_printf(("2ippReadIO: name=\"%s\", ipp->current=%p, ipp->prev=%p", buffer, (void *)ipp->current, (void *)ipp->prev)); +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 = attr->values; - } - else - { - attr = NULL; - value = NULL; - } - if ((*cb)(src, buffer, 2) < 2) - { - DEBUG_puts("1ippReadIO: unable to read value length."); - goto rollback; - } + /* + * Range check input... + */ - n = (buffer[0] << 8) | buffer[1]; - DEBUG_printf(("2ippReadIO: value length=%d", n)); + if (!ipp || !attr || !*attr) + return (0); - 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; - } + /* + * Set the value and return... + */ - 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; - } + if ((temp = _cupsStrAlloc(name)) != NULL) + { + if ((*attr)->name) + _cupsStrFree((*attr)->name); - if ((*cb)(src, buffer, 4) < 4) - { - DEBUG_puts("1ippReadIO: Unable to read integer value."); - goto rollback; - } + (*attr)->name = temp; + } - 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; - - 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; - } - - if ((*cb)(src, buffer, 1) < 1) - { - DEBUG_puts("1ippReadIO: Unable to read boolean value."); - goto rollback; - } + return (temp != NULL); +} - 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... - */ +/* + * 'ippSetOctetString()' - Set an octetString value in an IPP attribute. + * + * The @code ipp@ parameter refers to an IPP message previously created using + * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. + * + * The @code attr@ parameter may be modified as a result of setting the value. + * + * The @code element@ parameter specifies which value to set from 0 to + * @code ippGetCount(attr)@. + * + * @since CUPS 1.7/macOS 10.9@ + */ - if (attr->value_tag == tag) - { - if (n == 0) - break; +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 */ - attr->value_tag = IPP_TAG_TEXT; - } - 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; - } - } + /* + * Range check input... + */ - buffer[n] = '\0'; - value->string.text = _cupsStrAlloc((char *)buffer); - DEBUG_printf(("2ippReadIO: value=\"%s\"", value->string.text)); - break; + if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_STRING && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values || datalen < 0 || datalen > IPP_MAX_LENGTH) + return (0); - 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; - } + /* + * Set the value and return... + */ - if ((*cb)(src, value->date, 11) < 11) - { - DEBUG_puts("1ippReadIO: Unable to read date value."); - goto rollback; - } - break; + if ((value = ipp_set_value(ipp, attr, element)) != NULL) + { + if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST) + { + /* + * Just copy the pointer... + */ - 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; - } + value->unknown.data = (void *)data; + value->unknown.length = datalen; + } + else + { + /* + * Copy the data... + */ - if ((*cb)(src, buffer, 9) < 9) - { - DEBUG_puts("1ippReadIO: Unable to read resolution value."); - goto rollback; - } + (*attr)->value_tag = IPP_TAG_STRING; - 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 (value->unknown.data) + { + /* + * Free previous data... + */ - 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; - } + free(value->unknown.data); - if ((*cb)(src, buffer, 8) < 8) - { - DEBUG_puts("1ippReadIO: Unable to read range value."); - goto rollback; - } + value->unknown.data = NULL; + value->unknown.length = 0; + } - 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 (datalen > 0) + { + void *temp; /* Temporary data pointer */ - 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; - } + if ((temp = malloc((size_t)datalen)) != NULL) + { + memcpy(temp, data, (size_t)datalen); - if ((*cb)(src, buffer, (size_t)n) < n) - { - DEBUG_puts("1ippReadIO: Unable to read string w/language " - "value."); - goto rollback; - } + value->unknown.data = temp; + value->unknown.length = datalen; + } + else + return (0); + } + } + } - bufptr = buffer; - bufend = buffer + n; + return (value != NULL); +} - /* - * text-with-language and name-with-language are composite - * values: - * - * language-length - * language - * text-length - * text - */ - n = (bufptr[0] << 8) | bufptr[1]; +/* + * 'ippSetOperation()' - Set the operation ID in an IPP request message. + * + * The @code 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 ((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; - } +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... + */ - memcpy(string, bufptr + 2, (size_t)n); - string[n] = '\0'; + if (!ipp) + return (0); - value->string.language = _cupsStrAlloc((char *)string); + /* + * Set the operation and return... + */ - bufptr += 2 + n; - n = (bufptr[0] << 8) | bufptr[1]; + ipp->request.op.operation_id = op; - 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; - } - - bufptr[2 + n] = '\0'; - value->string.text = _cupsStrAlloc((char *)bufptr + 2); - break; - - 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; - } - - if (ippReadIO(src, cb, 1, ipp, value->collection) == IPP_STATE_ERROR) - { - DEBUG_puts("1ippReadIO: Unable to read collection value."); - goto rollback; - } - break; - - 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); - - DEBUG_puts("1ippReadIO: 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("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; - } - - 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 --; - - DEBUG_printf(("2ippReadIO: 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(("1ippReadIO: bad octetString value length %d.", - n)); - goto rollback; - } - - 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); -} + return (1); +} /* - * 'ippSetBoolean()' - Set a boolean value in an attribute. + * 'ippSetRange()' - Set a rangeOfInteger value in an attribute. * * The @code ipp@ parameter refers to an IPP message previously created using * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. @@ -3623,10 +3268,11 @@ ippReadIO(void *src, /* I - Data source */ */ 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 */ +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 */ @@ -3635,8 +3281,7 @@ ippSetBoolean(ipp_t *ipp, /* I - IPP message */ * 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_RANGE && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values || lowervalue > uppervalue) return (0); /* @@ -3644,63 +3289,52 @@ ippSetBoolean(ipp_t *ipp, /* I - IPP message */ */ if ((value = ipp_set_value(ipp, attr, element)) != NULL) - value->boolean = (char)boolvalue; + { + (*attr)->value_tag = IPP_TAG_RANGE; + value->range.lower = lowervalue; + value->range.upper = uppervalue; + } return (value != NULL); } /* - * 'ippSetCollection()' - Set a collection value in an attribute. + * 'ippSetRequestId()' - Set the request ID in an IPP message. * * The @code ipp@ parameter refers to an IPP message previously created using * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. * - * The @code attr@ parameter may be modified as a result of setting the value. - * - * The @code element@ parameter specifies which value to set from 0 to - * @code ippGetCount(attr)@. + * The @code request_id@ parameter must be greater than 0. * * @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 */ +ippSetRequestId(ipp_t *ipp, /* I - IPP message */ + int request_id) /* I - Request ID */ { - _ipp_value_t *value; /* Current value */ - - /* - * Range check input... + * 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 || !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... + * Set the request ID and return... */ - if ((value = ipp_set_value(ipp, attr, element)) != NULL) - { - if (value->collection) - ippDelete(value->collection); - - value->collection = colvalue; - colvalue->use ++; - } + ipp->request.any.request_id = request_id; - return (value != NULL); + return (1); } /* - * 'ippSetDate()' - Set a dateTime value in an attribute. + * 'ippSetResolution()' - Set a resolution value in an attribute. * * The @code ipp@ parameter refers to an IPP message previously created using * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. @@ -3714,10 +3348,13 @@ 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 */ +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 */ @@ -3726,7 +3363,7 @@ ippSetDate(ipp_t *ipp, /* I - IPP message */ * 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 (!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); /* @@ -3734,144 +3371,77 @@ ippSetDate(ipp_t *ipp, /* I - IPP message */ */ if ((value = ipp_set_value(ipp, attr, element)) != NULL) - memcpy(value->date, datevalue, sizeof(value->date)); + { + (*attr)->value_tag = IPP_TAG_RESOLUTION; + value->resolution.units = unitsvalue; + value->resolution.xres = xresvalue; + value->resolution.yres = yresvalue; + } return (value != NULL); } /* - * 'ippSetGroupTag()' - Set the group tag of an attribute. - * - * The @code ipp@ parameter refers to an IPP message previously created using - * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. - * - * The @code attr@ parameter may be modified as a result of setting the value. - * - * The @code group@ parameter specifies the IPP attribute group tag: none - * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@), - * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation - * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription - * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@). + * 'ippSetState()' - Set the current state of the IPP message. * * @since CUPS 1.6/macOS 10.8@ */ -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 */ +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 - group tag must be 0x01 to 0x0F, per RFC 8011... + * Range check input... */ - if (!ipp || !attr || !*attr || - group_tag < IPP_TAG_ZERO || group_tag == IPP_TAG_END || - group_tag >= IPP_TAG_UNSUPPORTED_VALUE) + if (!ipp) return (0); /* - * Set the group tag and return... + * Set the state and return... */ - (*attr)->group_tag = group_tag; + ipp->state = state; + ipp->current = NULL; return (1); } /* - * 'ippSetInteger()' - Set an integer or enum value in an attribute. + * 'ippSetStatusCode()' - Set the status code in an IPP response or event message. * * The @code ipp@ parameter refers to an IPP message previously created using * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. * - * The @code attr@ parameter may be modified as a result of setting the value. - * - * The @code 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 */ -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 */ - - - /* - * 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); - - /* - * 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; - - value->integer = intvalue; - } - - return (value != NULL); -} - - -/* - * 'ippSetName()' - Set the name of an attribute. - * - * The @code ipp@ parameter refers to an IPP message previously created using - * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. - * - * The @code attr@ parameter may be modified as a result of setting the value. - * - * @since CUPS 1.6/macOS 10.8@ - */ - -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 */ +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 */ { - char *temp; /* Temporary name value */ - - /* * Range check input... */ - if (!ipp || !attr || !*attr) + if (!ipp) return (0); /* - * Set the value and return... + * Set the status code and return... */ - if ((temp = _cupsStrAlloc(name)) != NULL) - { - if ((*attr)->name) - _cupsStrFree((*attr)->name); - - (*attr)->name = temp; - } + ipp->request.status.status_code = status; - return (temp != NULL); + return (1); } /* - * 'ippSetOctetString()' - Set an octetString value in an IPP attribute. + * 'ippSetString()' - Set a string value in an attribute. * * The @code ipp@ parameter refers to an IPP message previously created using * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. @@ -3881,25 +3451,30 @@ ippSetName(ipp_t *ipp, /* I - IPP message */ * The @code element@ parameter specifies which value to set from 0 to * @code ippGetCount(attr)@. * - * @since CUPS 1.7/macOS 10.9@ + * @since CUPS 1.6/macOS 10.8@ */ 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 */ +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_STRING && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values || datalen < 0 || datalen > IPP_MAX_LENGTH) + 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); /* @@ -3908,50 +3483,23 @@ ippSetOctetString( if ((value = ipp_set_value(ipp, attr, element)) != NULL) { + 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) { - /* - * Just copy the pointer... - */ + if (value->string.text) + _cupsStrFree(value->string.text); - value->unknown.data = (void *)data; - value->unknown.length = datalen; + value->string.text = temp; } else - { - /* - * Copy the data... - */ - - (*attr)->value_tag = IPP_TAG_STRING; - - if (value->unknown.data) - { - /* - * Free previous data... - */ - - free(value->unknown.data); - - value->unknown.data = NULL; - value->unknown.length = 0; - } - - if (datalen > 0) - { - void *temp; /* Temporary data pointer */ - - if ((temp = malloc((size_t)datalen)) != NULL) - { - memcpy(temp, data, (size_t)datalen); - - value->unknown.data = temp; - value->unknown.length = datalen; - } - else - return (0); - } - } + return (0); } return (value != NULL); @@ -3959,37 +3507,45 @@ ippSetOctetString( /* - * 'ippSetOperation()' - Set the operation ID in an IPP request message. + * 'ippSetStringf()' - Set a formatted string value of an attribute. * * The @code 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 @code attr@ parameter may be modified as a result of setting the value. + * + * The @code element@ parameter specifies which value to set from 0 to + * @code ippGetCount(attr)@. + * + * The @code 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 */ -ippSetOperation(ipp_t *ipp, /* I - IPP request message */ - ipp_op_t op) /* I - Operation ID */ +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 */ { - /* - * Range check input... - */ - - if (!ipp) - return (0); + int ret; /* Return value */ + va_list ap; /* Pointer to additional arguments */ - /* - * Set the operation and return... - */ - ipp->request.op.operation_id = op; + va_start(ap, format); + ret = ippSetStringfv(ipp, attr, element, format, ap); + va_end(ap); - return (1); + return (ret); } /* - * 'ippSetRange()' - Set a rangeOfInteger value in an attribute. + * 'ippSetStringf()' - Set a formatted string value of an attribute. * * The @code ipp@ parameter refers to an IPP message previously created using * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. @@ -3999,591 +3555,890 @@ ippSetOperation(ipp_t *ipp, /* I - IPP request message */ * The @code element@ parameter specifies which value to set from 0 to * @code ippGetCount(attr)@. * - * @since CUPS 1.6/macOS 10.8@ + * The @code 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 */ -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 */ +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_RANGE && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values || lowervalue > uppervalue) + 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 || !format) return (0); /* - * Set the value and return... + * Format the string... */ - if ((value = ipp_set_value(ipp, attr, element)) != NULL) + if (!strcmp(format, "%s")) { - (*attr)->value_tag = IPP_TAG_RANGE; - value->range.lower = lowervalue; - value->range.upper = uppervalue; - } + /* + * Optimize the simple case... + */ - return (value != NULL); -} + const char *s = va_arg(ap, char *); + + if (!s) + s = "(null)"; + bytes = (ssize_t)strlen(s); + strlcpy(buffer, s, sizeof(buffer)); + } + else + { + /* + * Do a full formatting of the message... + */ -/* - * 'ippSetRequestId()' - Set the request ID in an IPP message. - * - * The @code ipp@ parameter refers to an IPP message previously created using - * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. - * - * The @code request_id@ parameter must be greater than 0. - * - * @since CUPS 1.6/macOS 10.8@ - */ + if ((bytes = vsnprintf(buffer, sizeof(buffer), format, ap)) < 0) + return (0); + } -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... + * Limit the length of the string... */ - if (!ipp) - return (0); + 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; + + case IPP_TAG_CHARSET : + max_bytes = IPP_MAX_CHARSET; + break; + + 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; + + case IPP_TAG_URI : + max_bytes = IPP_MAX_URI; + 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; + + while (bufptr > bufmax) + { + if (*bufptr & 0x80) + { + while ((*bufptr & 0xc0) == 0x80 && bufptr > buffer) + bufptr --; + } + + bufptr --; + } + + *bufptr = '\0'; + } /* - * Set the request ID and return... + * Set the formatted string and return... */ - ipp->request.any.request_id = request_id; - - return (1); + return (ippSetString(ipp, attr, element, buffer)); } /* - * 'ippSetResolution()' - Set a resolution value in an attribute. + * 'ippSetValueTag()' - Set the value tag of an attribute. * * The @code ipp@ parameter refers to an IPP message previously created using * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. * * The @code attr@ parameter may be modified as a result of setting the value. * - * The @code element@ parameter specifies which value to set from 0 to - * @code ippGetCount(attr)@. + * Integer (@code IPP_TAG_INTEGER@) values can be promoted to rangeOfInteger + * (@code IPP_TAG_RANGE@) values, the various string tags can be promoted to name + * (@code IPP_TAG_NAME@) or nameWithLanguage (@code IPP_TAG_NAMELANG@) values, text + * (@code IPP_TAG_TEXT@) values can be promoted to textWithLanguage + * (@code IPP_TAG_TEXTLANG@) values, and all values can be demoted to the various + * out-of-band value tags such as no-value (@code 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@ */ int /* O - 1 on success, 0 on failure */ -ippSetResolution( - 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) */ - ipp_res_t unitsvalue, /* I - Resolution units */ - int xresvalue, /* I - Horizontal/cross feed resolution */ - int yresvalue) /* I - Vertical/feed resolution */ + 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_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) + if (!ipp || !attr || !*attr) return (0); /* - * Set the value and return... + * If there is no change, return immediately... */ - if ((value = ipp_set_value(ipp, attr, element)) != NULL) + if (value_tag == (*attr)->value_tag) + return (1); + + /* + * Otherwise implement changes as needed... + */ + + temp_tag = (ipp_tag_t)((int)((*attr)->value_tag) & IPP_TAG_CUPS_MASK); + + switch (value_tag) { - (*attr)->value_tag = IPP_TAG_RESOLUTION; - value->resolution.units = unitsvalue; - value->resolution.xres = xresvalue; - value->resolution.yres = yresvalue; - } + 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... + */ - return (value != NULL); -} + if ((*attr)->num_values > 0) + ipp_free_values(*attr, 0, (*attr)->num_values); + /* + * Set out-of-band value... + */ -/* - * 'ippSetState()' - Set the current state of the IPP message. - * - * @since CUPS 1.6/macOS 10.8@ - */ + (*attr)->value_tag = value_tag; + break; -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... - */ + case IPP_TAG_RANGE : + if (temp_tag != IPP_TAG_INTEGER) + return (0); - if (!ipp) - return (0); + for (i = (*attr)->num_values, value = (*attr)->values; + i > 0; + i --, value ++) + { + integer = value->integer; + value->range.lower = value->range.upper = integer; + } - /* - * Set the state and return... - */ + (*attr)->value_tag = IPP_TAG_RANGE; + break; - ipp->state = state; - ipp->current = NULL; + 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_NAMELANG : + case IPP_TAG_TEXTLANG : + if (value_tag == IPP_TAG_NAMELANG && (temp_tag != IPP_TAG_NAME && temp_tag != IPP_TAG_KEYWORD)) + return (0); + + if (value_tag == IPP_TAG_TEXTLANG && temp_tag != IPP_TAG_TEXT) + return (0); + + if (ipp->attrs && ipp->attrs->next && ipp->attrs->next->name && + !strcmp(ipp->attrs->next->name, "attributes-natural-language") && (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))); + } + + for (i = (*attr)->num_values - 1, value = (*attr)->values + 1; + i > 0; + i --, value ++) + value->string.language = (*attr)->values[0].string.language; + + if ((int)(*attr)->value_tag & IPP_TAG_CUPS_CONST) + { + /* + * Make copies of all values... + */ + + for (i = (*attr)->num_values, value = (*attr)->values; + i > 0; + i --, value ++) + value->string.text = _cupsStrAlloc(value->string.text); + } + + (*attr)->value_tag = IPP_TAG_NAMELANG; + break; + + case IPP_TAG_KEYWORD : + if (temp_tag == IPP_TAG_NAME || temp_tag == IPP_TAG_NAMELANG) + break; /* Silently "allow" name -> keyword */ + + default : + return (0); + } return (1); } /* - * 'ippSetStatusCode()' - Set the status code in an IPP response or event message. + * 'ippSetVersion()' - Set the version number in an IPP message. * * The @code ipp@ parameter refers to an IPP message previously created using * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. * + * The valid version numbers are currently 1.0, 1.1, 2.0, 2.1, and 2.2. + * * @since CUPS 1.6/macOS 10.8@ */ int /* O - 1 on success, 0 on failure */ -ippSetStatusCode(ipp_t *ipp, /* I - IPP response or event message */ - ipp_status_t status) /* I - Status code */ +ippSetVersion(ipp_t *ipp, /* I - IPP message */ + int major, /* I - Major version number (major.minor) */ + int minor) /* I - Minor version number (major.minor) */ { /* * Range check input... */ - if (!ipp) + if (!ipp || major < 0 || minor < 0) return (0); /* - * Set the status code and return... + * Set the version number... */ - ipp->request.status.status_code = status; + ipp->request.any.version[0] = (ipp_uchar_t)major; + ipp->request.any.version[1] = (ipp_uchar_t)minor; return (1); } /* - * 'ippSetString()' - Set a string value in an attribute. - * - * The @code ipp@ parameter refers to an IPP message previously created using - * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. - * - * The @code attr@ parameter may be modified as a result of setting the value. - * - * The @code 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 */ -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 */ +const ipp_uchar_t * /* O - RFC-2579 date/time data */ +ippTimeToDate(time_t t) /* I - Time in seconds */ { - char *temp; /* Temporary string */ - _ipp_value_t *value; /* Current value */ - ipp_tag_t value_tag; /* Value tag */ - - - /* - * Range check input... - */ - - if (attr && *attr) - value_tag = (*attr)->value_tag & IPP_TAG_CUPS_MASK; - else - value_tag = IPP_TAG_ZERO; + struct tm unixdate; /* UNIX unixdate/time info */ + ipp_uchar_t *date = _cupsGlobals()->ipp_date; + /* RFC-2579 date/time data */ - 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... + * 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) */ - if ((value = ipp_set_value(ipp, attr, element)) != NULL) - { - 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); -} - - -/* - * 'ippSetStringf()' - Set a formatted string value of an attribute. - * - * The @code ipp@ parameter refers to an IPP message previously created using - * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. - * - * The @code attr@ parameter may be modified as a result of setting the value. - * - * The @code element@ parameter specifies which value to set from 0 to - * @code ippGetCount(attr)@. - * - * The @code 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 */ - + gmtime_r(&t, &unixdate); + unixdate.tm_year += 1900; - va_start(ap, format); - ret = ippSetStringfv(ipp, attr, element, format, ap); - va_end(ap); + 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 (ret); + return (date); } /* - * 'ippSetStringf()' - Set a formatted string value of an attribute. - * - * The @code ipp@ parameter refers to an IPP message previously created using - * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. - * - * The @code attr@ parameter may be modified as a result of setting the value. - * - * The @code element@ parameter specifies which value to set from 0 to - * @code ippGetCount(attr)@. + * 'ippValidateAttribute()' - Validate the contents of an attribute. * - * The @code 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. + * This function validates the contents of an attribute based on the name and + * value tag. 1 is returned if the attribute is valid, 0 otherwise. On + * failure, @link cupsLastErrorString@ is set to a human-readable message. * * @since CUPS 1.7/macOS 10.9@ */ -int /* O - 1 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 */ +int /* O - 1 if valid, 0 otherwise */ +ippValidateAttribute( + ipp_attribute_t *attr) /* I - Attribute */ { - 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 */ + int i; /* Looping var */ + char scheme[64], /* Scheme from URI */ + userpass[256], /* Username/password from URI */ + hostname[256], /* Hostname from URI */ + resource[1024]; /* Resource from URI */ + int port, /* Port number from URI */ + uri_status; /* URI separation status */ + const char *ptr; /* Pointer into string */ + ipp_attribute_t *colattr; /* Collection attribute */ + regex_t re; /* Regular expression */ + ipp_uchar_t *date; /* Current date value */ /* - * Range check input... + * Skip separators. */ - 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 || !format) - return (0); + if (!attr->name) + return (1); /* - * Format the string... + * Validate the attribute name. */ - if (!strcmp(format, "%s")) - { - /* - * Optimize the simple case... - */ - - const char *s = va_arg(ap, char *); - - if (!s) - s = "(null)"; + for (ptr = attr->name; *ptr; ptr ++) + if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_') + break; - bytes = (ssize_t)strlen(s); - strlcpy(buffer, s, sizeof(buffer)); - } - else + if (*ptr || ptr == attr->name) { - /* - * Do a full formatting of the message... - */ - - if ((bytes = vsnprintf(buffer, sizeof(buffer), format, ap)) < 0) - return (0); + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad attribute name - invalid character (RFC 8011 section 5.1.4)."), attr->name); + return (0); } - /* - * Limit the length of the string... - */ + if ((ptr - attr->name) > 255) + { + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad attribute name - bad length %d (RFC 8011 section 5.1.4)."), attr->name, (int)(ptr - attr->name)); + return (0); + } - switch (value_tag) + switch (attr->value_tag) { - default : - case IPP_TAG_TEXT : - case IPP_TAG_TEXTLANG : - max_bytes = IPP_MAX_TEXT; + case IPP_TAG_INTEGER : break; - case IPP_TAG_NAME : - case IPP_TAG_NAMELANG : - max_bytes = IPP_MAX_NAME; + case IPP_TAG_BOOLEAN : + for (i = 0; i < attr->num_values; i ++) + { + if (attr->values[i].boolean != 0 && + attr->values[i].boolean != 1) + { + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad boolean value %d (RFC 8011 section 5.1.21)."), attr->name, attr->values[i].boolean); + return (0); + } + } break; - case IPP_TAG_CHARSET : - max_bytes = IPP_MAX_CHARSET; + case IPP_TAG_ENUM : + for (i = 0; i < attr->num_values; i ++) + { + if (attr->values[i].integer < 1) + { + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad enum value %d - out of range (RFC 8011 section 5.1.5)."), attr->name, attr->values[i].integer); + return (0); + } + } break; - case IPP_TAG_NOVALUE : - case IPP_TAG_UNKNOWN : - case IPP_TAG_KEYWORD : - max_bytes = IPP_MAX_KEYWORD; + 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_LANGUAGE : - max_bytes = IPP_MAX_LANGUAGE; - break; + case IPP_TAG_DATE : + for (i = 0; i < attr->num_values; i ++) + { + date = attr->values[i].date; - case IPP_TAG_MIMETYPE : - max_bytes = IPP_MAX_MIMETYPE; - break; + 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); + } - case IPP_TAG_URI : - max_bytes = IPP_MAX_URI; - break; + 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); + } - case IPP_TAG_URISCHEME : - max_bytes = IPP_MAX_URISCHEME; - break; - } + 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 (bytes >= max_bytes) - { - char *bufmax, /* Buffer at max_bytes */ - *bufptr; /* Pointer into buffer */ + 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); + } - bufptr = buffer + strlen(buffer) - 1; - bufmax = buffer + max_bytes - 1; + 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); + } - while (bufptr > bufmax) - { - if (*bufptr & 0x80) - { - while ((*bufptr & 0xc0) == 0x80 && bufptr > buffer) - bufptr --; - } + 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); + } - bufptr --; - } + 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); + } - *bufptr = '\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); + } - /* - * Set the formatted string and return... - */ + 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; - return (ippSetString(ipp, attr, element, buffer)); -} + case IPP_TAG_RESOLUTION : + for (i = 0; i < attr->num_values; i ++) + { + if (attr->values[i].resolution.xres <= 0) + { + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad resolution value %dx%d%s - cross feed resolution must be positive (RFC 8011 section 5.1.16)."), attr->name, attr->values[i].resolution.xres, attr->values[i].resolution.yres, attr->values[i].resolution.units == IPP_RES_PER_INCH ? "dpi" : attr->values[i].resolution.units == IPP_RES_PER_CM ? "dpcm" : "unknown"); + return (0); + } + if (attr->values[i].resolution.yres <= 0) + { + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad resolution value %dx%d%s - feed resolution must be positive (RFC 8011 section 5.1.16)."), attr->name, attr->values[i].resolution.xres, attr->values[i].resolution.yres, attr->values[i].resolution.units == IPP_RES_PER_INCH ? "dpi" : attr->values[i].resolution.units == IPP_RES_PER_CM ? "dpcm" : "unknown"); + return (0); + } -/* - * 'ippSetValueTag()' - Set the value tag of an attribute. - * - * The @code ipp@ parameter refers to an IPP message previously created using - * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. - * - * The @code attr@ parameter may be modified as a result of setting the value. - * - * Integer (@code IPP_TAG_INTEGER@) values can be promoted to rangeOfInteger - * (@code IPP_TAG_RANGE@) values, the various string tags can be promoted to name - * (@code IPP_TAG_NAME@) or nameWithLanguage (@code IPP_TAG_NAMELANG@) values, text - * (@code IPP_TAG_TEXT@) values can be promoted to textWithLanguage - * (@code IPP_TAG_TEXTLANG@) values, and all values can be demoted to the various - * out-of-band value tags such as no-value (@code 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@ - */ + 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; -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 */ + case IPP_TAG_RANGE : + for (i = 0; i < attr->num_values; i ++) + { + if (attr->values[i].range.lower > attr->values[i].range.upper) + { + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad rangeOfInteger value %d-%d - lower greater than upper (RFC 8011 section 5.1.14)."), attr->name, attr->values[i].range.lower, attr->values[i].range.upper); + return (0); + } + } + break; + case IPP_TAG_BEGIN_COLLECTION : + for (i = 0; i < attr->num_values; i ++) + { + for (colattr = attr->values[i].collection->attrs; + colattr; + colattr = colattr->next) + { + if (!ippValidateAttribute(colattr)) + return (0); + } + } + break; - /* - * Range check input... - */ + 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; - if (!ipp || !attr || !*attr) - return (0); + ptr ++; + } + else if ((*ptr & 0xf0) == 0xe0) + { + if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80) + break; - /* - * If there is no change, return immediately... - */ + ptr += 2; + } + else if ((*ptr & 0xf8) == 0xf0) + { + if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80 || (ptr[3] & 0xc0) != 0x80) + break; - if (value_tag == (*attr)->value_tag) - return (1); + ptr += 3; + } + else if (*ptr & 0x80) + break; + else if ((*ptr < ' ' && *ptr != '\n' && *ptr != '\r' && *ptr != '\t') || *ptr == 0x7f) + break; + } - /* - * Otherwise implement changes as needed... - */ + 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); + } + } - temp_tag = (ipp_tag_t)((int)((*attr)->value_tag) & IPP_TAG_CUPS_MASK); + 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; - 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... - */ + 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; - if ((*attr)->num_values > 0) - ipp_free_values(*attr, 0, (*attr)->num_values); + ptr ++; + } + else if ((*ptr & 0xf0) == 0xe0) + { + if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80) + break; - /* - * Set out-of-band value... - */ + ptr += 2; + } + else if ((*ptr & 0xf8) == 0xf0) + { + if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80 || (ptr[3] & 0xc0) != 0x80) + break; - (*attr)->value_tag = value_tag; + 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_RANGE : - if (temp_tag != IPP_TAG_INTEGER) - return (0); + 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; + } - for (i = (*attr)->num_values, value = (*attr)->values; - i > 0; - i --, value ++) - { - integer = value->integer; - value->range.lower = value->range.upper = integer; - } + 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); + } - (*attr)->value_tag = IPP_TAG_RANGE; + 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_NAME : - if (temp_tag != IPP_TAG_KEYWORD) - 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)); - (*attr)->value_tag = (ipp_tag_t)(IPP_TAG_NAME | ((*attr)->value_tag & IPP_TAG_CUPS_CONST)); + 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_NAMELANG : - case IPP_TAG_TEXTLANG : - if (value_tag == IPP_TAG_NAMELANG && (temp_tag != IPP_TAG_NAME && temp_tag != IPP_TAG_KEYWORD)) - return (0); + 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 (value_tag == IPP_TAG_TEXTLANG && temp_tag != IPP_TAG_TEXT) - 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 (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... - */ + 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; - (*attr)->values[0].string.language = - _cupsStrAlloc(ipp->attrs->next->values[0].string.text); - } - else + 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 ((i = 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) { - /* - * Otherwise, use the language code corresponding to the locale... - */ + char temp[256]; /* Temporary error string */ - language = cupsLangDefault(); - (*attr)->values[0].string.language = _cupsStrAlloc(ipp_lang_code(language->language, - code, - sizeof(code))); + regerror(i, &re, temp, sizeof(temp)); + ipp_set_error(IPP_STATUS_ERROR_INTERNAL, _("Unable to compile naturalLanguage regular expression: %s."), temp); + return (0); } - for (i = (*attr)->num_values - 1, value = (*attr)->values + 1; - i > 0; - i --, value ++) - value->string.language = (*attr)->values[0].string.language; + 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 ((int)(*attr)->value_tag & IPP_TAG_CUPS_CONST) + 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 ((i = 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) { - /* - * Make copies of all values... - */ + char temp[256]; /* Temporary error string */ - for (i = (*attr)->num_values, value = (*attr)->values; - i > 0; - i --, value ++) - value->string.text = _cupsStrAlloc(value->string.text); + regerror(i, &re, temp, sizeof(temp)); + ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("Unable to compile mimeMediaType 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 mimeMediaType value \"%s\" - bad characters (RFC 8011 section 5.1.10)."), 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_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); @@ -4591,2057 +4446,2227 @@ ippSetValueTag( /* - * 'ippSetVersion()' - Set the version number in an IPP message. + * 'ippValidateAttributes()' - Validate all attributes in an IPP message. * - * The @code ipp@ parameter refers to an IPP message previously created using - * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions. - * - * The valid version numbers are currently 1.0, 1.1, 2.0, 2.1, and 2.2. + * This function validates the contents of the IPP message, including each + * attribute. Like @link ippValidateAttribute@, @link cupsLastErrorString@ 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... - */ + ipp_attribute_t *attr; /* Current attribute */ - if (!ipp || major < 0 || minor < 0) - return (0); - /* - * Set the version number... - */ + if (!ipp) + return (1); - ipp->request.any.version[0] = (ipp_uchar_t)major; - ipp->request.any.version[1] = (ipp_uchar_t)minor; + 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 cupsLastErrorString@ 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 */ - 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. - */ + return (ippWriteIO(&fd, (ipp_iocb_t)ipp_write_file, 1, NULL, ipp)); +} - if (!attr->name) - return (1); - /* - * Validate the attribute name. - */ +/* + * 'ippWriteIO()' - Write data for an IPP message. + * + * @since CUPS 1.2/macOS 10.5@ + */ - for (ptr = attr->name; *ptr; ptr ++) - if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_') - break; +ipp_state_t /* O - Current state */ +ippWriteIO(void *dst, /* I - Destination */ + ipp_iocb_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 */ - if (*ptr || ptr == attr->name) - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad attribute name - invalid character (RFC 8011 section 5.1.4)."), attr->name); - return (0); - } - if ((ptr - attr->name) > 255) + 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) { - 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); + 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 + */ - 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 = buffer; - 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; + *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_DATE : - for (i = 0; i < attr->num_values; i ++) - { - date = attr->values[i].date; + 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 (date[2] < 1 || date[2] > 12) + if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) { - 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); + DEBUG_puts("1ippWriteIO: Could not write IPP header..."); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); } + } - 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); - } + /* + * Reset the state engine to point to the first attribute + * in the request/response, with no current group. + */ - 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); - } + ipp->state = IPP_STATE_ATTRIBUTE; + ipp->current = ipp->attrs; + ipp->curtag = 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); - } + DEBUG_printf(("1ippWriteIO: ipp->current=%p", (void *)ipp->current)); - if (date[6] > 60) - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime seconds %u (RFC 8011 section 5.1.15)."), attr->name, date[6]); - return (0); - } + /* + * If blocking is disabled, stop here... + */ - if (date[7] > 9) - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime deciseconds %u (RFC 8011 section 5.1.15)."), attr->name, date[7]); - return (0); - } - - if (date[8] != '-' && date[8] != '+') - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime UTC sign '%c' (RFC 8011 section 5.1.15)."), attr->name, date[8]); - return (0); - } - - if (date[9] > 11) - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime UTC hours %u (RFC 8011 section 5.1.15)."), attr->name, date[9]); - return (0); - } - - if (date[10] > 59) - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime UTC minutes %u (RFC 8011 section 5.1.15)."), attr->name, date[10]); - return (0); - } - } - break; + if (!blocking) + break; - case IPP_TAG_RESOLUTION : - for (i = 0; i < attr->num_values; i ++) + case IPP_STATE_ATTRIBUTE : + while (ipp->current != NULL) { - if (attr->values[i].resolution.xres <= 0) - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad resolution value %dx%d%s - cross feed resolution must be positive (RFC 8011 section 5.1.16)."), attr->name, attr->values[i].resolution.xres, attr->values[i].resolution.yres, attr->values[i].resolution.units == IPP_RES_PER_INCH ? "dpi" : attr->values[i].resolution.units == IPP_RES_PER_CM ? "dpcm" : "unknown"); - return (0); - } - - if (attr->values[i].resolution.yres <= 0) - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad resolution value %dx%d%s - feed resolution must be positive (RFC 8011 section 5.1.16)."), attr->name, attr->values[i].resolution.xres, attr->values[i].resolution.yres, attr->values[i].resolution.units == IPP_RES_PER_INCH ? "dpi" : attr->values[i].resolution.units == IPP_RES_PER_CM ? "dpcm" : "unknown"); - return (0); - } - - if (attr->values[i].resolution.units != IPP_RES_PER_INCH && attr->values[i].resolution.units != IPP_RES_PER_CM) - { - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad resolution value %dx%d%s - bad units value (RFC 8011 section 5.1.16)."), attr->name, attr->values[i].resolution.xres, attr->values[i].resolution.yres, attr->values[i].resolution.units == IPP_RES_PER_INCH ? "dpi" : attr->values[i].resolution.units == IPP_RES_PER_CM ? "dpcm" : "unknown"); - return (0); - } - } - break; + /* + * Write this attribute... + */ - 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; + bufptr = buffer; + attr = ipp->current; - 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; + ipp->current = ipp->current->next; - 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 (!parent) { - if ((*ptr & 0xe0) == 0xc0) + if (ipp->curtag != attr->group_tag) { - if ((ptr[1] & 0xc0) != 0x80) - break; + /* + * Send a group tag byte... + */ - ptr ++; - } - else if ((*ptr & 0xf0) == 0xe0) - { - if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80) - break; + ipp->curtag = attr->group_tag; - ptr += 2; - } - else if ((*ptr & 0xf8) == 0xf0) - { - if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80 || (ptr[3] & 0xc0) != 0x80) - break; + if (attr->group_tag == IPP_TAG_ZERO) + continue; - ptr += 3; + DEBUG_printf(("2ippWriteIO: wrote group tag=%x(%s)", + attr->group_tag, ippTagString(attr->group_tag))); + *bufptr++ = (ipp_uchar_t)attr->group_tag; } - else if (*ptr & 0x80) - break; - else if ((*ptr < ' ' && *ptr != '\n' && *ptr != '\r' && *ptr != '\t') || *ptr == 0x7f) - break; + else if (attr->group_tag == IPP_TAG_ZERO) + continue; } - 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); - } - } + DEBUG_printf(("1ippWriteIO: %s (%s%s)", attr->name, + attr->num_values > 1 ? "1setOf " : "", + ippTagString(attr->value_tag))); - 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; + /* + * 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... + */ - 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 (parent == NULL) { - if ((*ptr & 0xe0) == 0xc0) - { - if ((ptr[1] & 0xc0) != 0x80) - break; + /* + * Get the length of the attribute name, and make sure it won't + * overflow the buffer... + */ - ptr ++; - } - else if ((*ptr & 0xf0) == 0xe0) + if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 8)) { - if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80) - break; - - ptr += 2; + DEBUG_printf(("1ippWriteIO: Attribute name too long (%d)", n)); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); } - 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; - } + /* + * Write the value tag, name length, and name string... + */ - if (*ptr) + 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 (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; + + *bufptr++ = (ipp_uchar_t)(n >> 8); + *bufptr++ = (ipp_uchar_t)n; + memcpy(bufptr, attr->name, (size_t)n); + bufptr += n; + } + else { - 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 + /* + * 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)) { - 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); + DEBUG_printf(("1ippWriteIO: Attribute name too long (%d)", n)); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); } - } - 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; + /* + * Write the member name tag, name length, name string, value tag, + * and empty name for the collection member attribute... + */ - 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; - } + 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,\"\""); - 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); - } + *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 ((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); + 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; + + *bufptr++ = 0; + *bufptr++ = 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)); + /* + * Now write the attribute value(s)... + */ - if (uri_status < HTTP_URI_STATUS_OK) + switch (attr->value_tag & ~IPP_TAG_CUPS_CONST) { - 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); - } + 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; - 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_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); + } - 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; - } - } + 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; + } + + /* + * 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; + + 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); + } + + 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; + } + + /* + * 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; + + 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,\"\"")); + + 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; + } + + if (value->string.text != NULL) + n = (int)strlen(value->string.text); + else + n = 0; + + 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)); + + 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; + } + + /* + * 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; + + if (n > 0) + { + memcpy(bufptr, value->string.text, (size_t)n); + bufptr += n; + } + } + 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); + } + + 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; + } + + /* + * 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); + } + + 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; + } + + /* + * 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; + + 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; + } + + 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; + } + + /* + * 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. + */ - 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); - } + *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 ((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_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... + */ - 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 ((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 (*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); - } + bufptr = buffer; + } - 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; + *bufptr++ = (ipp_uchar_t)attr->value_tag; + *bufptr++ = 0; + *bufptr++ = 0; + } - 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... - */ + /* + * 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. + */ - if ((i = 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 */ + n = 4; - regerror(i, &re, temp, sizeof(temp)); - ipp_set_error(IPP_STATUS_ERROR_INTERNAL, _("Unable to compile naturalLanguage regular expression: %s."), temp); - return (0); - } + if (value->string.language != NULL) + n += (int)strlen(value->string.language); - 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 (value->string.text != NULL) + n += (int)strlen(value->string.text); - 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); - } - } + if (n > (IPP_BUF_SIZE - 2)) + { + DEBUG_printf(("1ippWriteIO: text/nameWithLanguage value " + "too long (%d)", n)); + _cupsBufferRelease((char *)buffer); + return (IPP_STATE_ERROR); + } - regfree(&re); - break; + 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); + } - 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... - */ + bufptr = buffer; + } - if ((i = 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 */ + /* Length of entire value */ + *bufptr++ = (ipp_uchar_t)(n >> 8); + *bufptr++ = (ipp_uchar_t)n; - regerror(i, &re, temp, sizeof(temp)); - ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("Unable to compile mimeMediaType regular expression: %s."), temp); - return (0); - } + /* Length of language */ + if (value->string.language != NULL) + n = (int)strlen(value->string.language); + else + n = 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); - } + *bufptr++ = (ipp_uchar_t)(n >> 8); + *bufptr++ = (ipp_uchar_t)n; - 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); - } - } + /* Language */ + if (n > 0) + { + memcpy(bufptr, value->string.language, (size_t)n); + bufptr += n; + } - regfree(&re); - break; + /* Length of text */ + if (value->string.text != NULL) + n = (int)strlen(value->string.text); + else + n = 0; - default : - break; - } + *bufptr++ = (ipp_uchar_t)(n >> 8); + *bufptr++ = (ipp_uchar_t)n; - return (1); -} + /* 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 ++) + { + /* + * 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... + */ -/* - * 'ippValidateAttributes()' - Validate all attributes in an IPP message. - * - * This function validates the contents of the IPP message, including each - * attribute. Like @link ippValidateAttribute@, @link cupsLastErrorString@ is - * set to a human-readable message on failure. - * - * @since CUPS 1.7/macOS 10.9@ - */ + 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); + } + + 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; + } - if (!ipp) - return (1); + /* + * Write a data length of 0 and flush the buffer... + */ - for (attr = ipp->attrs; attr; attr = attr->next) - if (!ippValidateAttribute(attr)) - return (0); + *bufptr++ = 0; + *bufptr++ = 0; - return (1); -} + 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; -/* - * 'ippWrite()' - Write data for an IPP message to a HTTP connection. - */ + /* + * Then write the collection attribute... + */ -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)); + value->collection->state = IPP_STATE_IDLE; - if (!http) - return (IPP_STATE_ERROR); + 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; - return (ippWriteIO(http, (ipp_iocb_t)httpWrite2, http->blocking, NULL, ipp)); -} + 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); + } -/* - * 'ippWriteFile()' - Write data for an IPP message to a file. - * - * @since CUPS 1.1.19/macOS 10.3@ - */ + bufptr = buffer; + } -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)); + *bufptr++ = (ipp_uchar_t)attr->value_tag; + *bufptr++ = 0; + *bufptr++ = 0; + } - ipp->state = IPP_STATE_IDLE; + /* + * 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. + */ - return (ippWriteIO(&fd, (ipp_iocb_t)ipp_write_file, 1, NULL, ipp)); -} + 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); + } -/* - * 'ippWriteIO()' - Write data for an IPP message. - * - * @since CUPS 1.2/macOS 10.5@ - */ + 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_state_t /* O - Current state */ -ippWriteIO(void *dst, /* I - Destination */ - ipp_iocb_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 */ + bufptr = buffer; + } + /* Length of unknown value */ + *bufptr++ = (ipp_uchar_t)(n >> 8); + *bufptr++ = (ipp_uchar_t)n; - DEBUG_printf(("ippWriteIO(dst=%p, cb=%p, blocking=%d, parent=%p, ipp=%p)", (void *)dst, (void *)cb, blocking, (void *)parent, (void *)ipp)); + /* Value */ + if (n > 0) + { + memcpy(bufptr, value->unknown.data, (size_t)n); + bufptr += n; + } + } + break; + } - if (!dst || !ipp) - return (IPP_STATE_ERROR); + /* + * Write the data out... + */ - if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL) - { - DEBUG_puts("1ippWriteIO: Unable to get write buffer"); - return (IPP_STATE_ERROR); - } + 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); + } - switch (ipp->state) - { - case IPP_STATE_IDLE : - ipp->state ++; /* Avoid common problem... */ + DEBUG_printf(("2ippWriteIO: wrote %d bytes", + (int)(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 + * If blocking is disabled and we aren't at the end of the attribute + * list, stop here... */ - bufptr = buffer; + if (!blocking && ipp->current) + 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; + if (ipp->current == NULL) + { + /* + * Done with all of the attributes; add the end-of-attributes + * tag or end-collection attribute... + */ - 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 (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)(bufptr - buffer)) < 0) + if ((*cb)(dst, buffer, (size_t)n) < 0) { - DEBUG_puts("1ippWriteIO: Could not write IPP header..."); + DEBUG_puts("1ippWriteIO: Could not write IPP end-tag..."); _cupsBufferRelease((char *)buffer); return (IPP_STATE_ERROR); } - } - - /* - * 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)); + ipp->state = IPP_STATE_DATA; + } + break; - /* - * If blocking is disabled, stop here... - */ + case IPP_STATE_DATA : + break; - if (!blocking) - break; + default : + break; /* anti-compiler-warning-code */ + } - case IPP_STATE_ATTRIBUTE : - while (ipp->current != NULL) - { - /* - * Write this attribute... - */ + _cupsBufferRelease((char *)buffer); - bufptr = buffer; - attr = ipp->current; + return (ipp->state); +} - ipp->current = ipp->current->next; - if (!parent) - { - if (ipp->curtag != attr->group_tag) - { - /* - * Send a group tag byte... - */ +/* + * 'ipp_add_attr()' - Add a new attribute to the message. + */ - ipp->curtag = attr->group_tag; +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 */ - if (attr->group_tag == IPP_TAG_ZERO) - continue; - 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; - } + 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)); - DEBUG_printf(("1ippWriteIO: %s (%s%s)", attr->name, - attr->num_values > 1 ? "1setOf " : "", - ippTagString(attr->value_tag))); + /* + * Range check input... + */ - /* - * 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 (!ipp || num_values < 0) + return (NULL); - if (parent == NULL) - { - /* - * Get the length of the attribute name, and make sure it won't - * overflow the buffer... - */ + /* + * Allocate memory, rounding the allocation up as needed... + */ - 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 (num_values <= 1) + alloc_values = 1; + else + alloc_values = (num_values + IPP_MAX_VALUES - 1) & ~(IPP_MAX_VALUES - 1); - /* - * Write the value tag, name length, and name string... - */ + attr = calloc(1, sizeof(ipp_attribute_t) + + (size_t)(alloc_values - 1) * sizeof(_ipp_value_t)); - 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 (attr) + { + /* + * Initialize attribute... + */ - 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; + DEBUG_printf(("4debug_alloc: %p %s %s%s (%d values)", (void *)attr, name, num_values > 1 ? "1setOf " : "", ippTagString(value_tag), num_values)); - *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 (name) + attr->name = _cupsStrAlloc(name); - 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); - } + attr->group_tag = group_tag; + attr->value_tag = value_tag; + attr->num_values = num_values; - /* - * Write the member name tag, name length, name string, value tag, - * and empty name for the collection member attribute... - */ + /* + * Add it to the end of the linked list... + */ - 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,\"\""); + if (ipp->last) + ipp->last->next = attr; + else + ipp->attrs = attr; - *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; + ipp->prev = ipp->last; + ipp->last = ipp->current = attr; + } - 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; + DEBUG_printf(("5ipp_add_attr: Returning %p", (void *)attr)); - *bufptr++ = 0; - *bufptr++ = 0; - } + return (attr); +} - /* - * 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; +/* + * 'ipp_free_values()' - Free attribute values. + */ - 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); - } +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 */ - bufptr = buffer; - } - if (i) - { - /* - * Arrays and sets are done by sending additional - * values with a zero-length name... - */ + DEBUG_printf(("4ipp_free_values(attr=%p, element=%d, count=%d)", (void *)attr, element, count)); - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } + if (!(attr->value_tag & IPP_TAG_CUPS_CONST)) + { + /* + * Free values as needed... + */ - /* - * Integers and enumerations are both 4-byte signed - * (twos-complement) values. - * - * Put the 2-byte length and 4-byte value into the buffer... - */ + 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++ = 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; + 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; - 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); - } + 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; - bufptr = buffer; - } + case IPP_TAG_BEGIN_COLLECTION : + for (i = count, value = attr->values + element; + i > 0; + i --, value ++) + { + ippDelete(value->collection); + value->collection = NULL; + } + break; - if (i) - { - /* - * Arrays and sets are done by sending additional - * values with a zero-length name... - */ + 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++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } + /* + * If we are not freeing values from the end, move the remaining values up... + */ - /* - * Boolean values are 1-byte; 0 = false, 1 = true. - * - * Put the 2-byte length and 1-byte value into the buffer... - */ + 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)); - *bufptr++ = 0; - *bufptr++ = 1; - *bufptr++ = (ipp_uchar_t)value->boolean; - } - break; + attr->num_values -= count; +} - 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_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. + */ - 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); - } +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 */ - bufptr = buffer; - } - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } + /* + * Convert values to lowercase and change _ to - as needed... + */ - if (value->string.text != NULL) - n = (int)strlen(value->string.text); - else - n = 0; + for (bufptr = buffer, bufend = buffer + bufsize - 1; + *value && bufptr < bufend; + value ++) + if (*value == '_') + *bufptr++ = '-'; + else + *bufptr++ = (char)_cups_tolower(*value); - if (n > (IPP_BUF_SIZE - 2)) - { - DEBUG_printf(("1ippWriteIO: String too long (%d)", n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + *bufptr = '\0'; - DEBUG_printf(("2ippWriteIO: writing string=%d,\"%s\"", n, - value->string.text)); + /* + * Return the converted string... + */ - 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 (buffer); +} - 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. - */ +/* + * '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". + */ - *bufptr++ = (ipp_uchar_t)(n >> 8); - *bufptr++ = (ipp_uchar_t)n; +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 (n > 0) - { - memcpy(bufptr, value->string.text, (size_t)n); - bufptr += n; - } - } - break; + if (!_cups_strcasecmp(locale, "c")) + { + strlcpy(buffer, "en", bufsize); + return (buffer); + } + else + return (ipp_get_code(locale, buffer, bufsize)); +} - 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; - } +/* + * 'ipp_length()' - Compute the length of an IPP message or collection value. + */ - if (i) - { - /* - * Arrays and sets are done by sending additional - * values with a zero-length name... - */ +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; - } - /* - * 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. - */ + DEBUG_printf(("3ipp_length(ipp=%p, collection=%d)", (void *)ipp, collection)); - *bufptr++ = 0; - *bufptr++ = 11; - memcpy(bufptr, value->date, 11); - bufptr += 11; - } - break; + if (!ipp) + { + DEBUG_puts("4ipp_length: Returning 0 bytes"); + return (0); + } - 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); - } + /* + * Start with 8 bytes for the IPP message header... + */ - bufptr = buffer; - } + bytes = collection ? 0 : 8; - if (i) - { - /* - * Arrays and sets are done by sending additional - * values with a zero-length name... - */ + /* + * Then add the lengths of each attribute... + */ - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } + group = IPP_TAG_ZERO; - /* - * 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. - */ + 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++ = 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; + bytes ++; /* Group tag */ + } - 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 (!attr->name) + continue; - bufptr = buffer; - } + DEBUG_printf(("5ipp_length: attr->name=\"%s\", attr->num_values=%d, " + "bytes=" CUPS_LLFMT, attr->name, attr->num_values, CUPS_LLCAST bytes)); - if (i) - { - /* - * Arrays and sets are done by sending additional - * values with a zero-length name... - */ + 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 */ - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } + if (collection) + bytes += 5; /* Add membername overhead */ - /* - * 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. - */ + 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++ = 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_INTEGER : + case IPP_TAG_ENUM : + bytes += (size_t)(4 * attr->num_values); + 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... - */ + case IPP_TAG_BOOLEAN : + bytes += (size_t)attr->num_values; + break; - 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_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; - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } + case IPP_TAG_RESOLUTION : + bytes += (size_t)(9 * attr->num_values); + break; - /* - * 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. - */ + case IPP_TAG_RANGE : + bytes += (size_t)(8 * attr->num_values); + break; - n = 4; + case IPP_TAG_TEXTLANG : + case IPP_TAG_NAMELANG : + bytes += (size_t)(4 * attr->num_values); + /* Charset + text length */ - if (value->string.language != NULL) - n += (int)strlen(value->string.language); + for (i = 0, value = attr->values; + i < attr->num_values; + i ++, value ++) + { + if (value->string.language) + bytes += strlen(value->string.language); - if (value->string.text != NULL) - n += (int)strlen(value->string.text); + if (value->string.text) + bytes += strlen(value->string.text); + } + 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 ++) + bytes += (size_t)value->unknown.length; + break; + } + } - if (n > (IPP_BUF_SIZE - 2)) - { - DEBUG_printf(("1ippWriteIO: text/nameWithLanguage value " - "too long (%d)", n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + /* + * Finally, add 1 byte for the "end of attributes" tag or 5 bytes + * for the "end of collection" tag and return... + */ - 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 (collection) + bytes += 5; + else + bytes ++; - bufptr = buffer; - } + DEBUG_printf(("4ipp_length: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST bytes)); - /* Length of entire value */ - *bufptr++ = (ipp_uchar_t)(n >> 8); - *bufptr++ = (ipp_uchar_t)n; + return (bytes); +} - /* Length of language */ - if (value->string.language != NULL) - n = (int)strlen(value->string.language); - else - n = 0; - *bufptr++ = (ipp_uchar_t)(n >> 8); - *bufptr++ = (ipp_uchar_t)n; +/* + * 'ipp_read_file()' - Read IPP data from a file. + */ - /* Language */ - if (n > 0) - { - memcpy(bufptr, value->string.language, (size_t)n); - bufptr += n; - } +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 text */ - if (value->string.text != NULL) - n = (int)strlen(value->string.text); - else - n = 0; - *bufptr++ = (ipp_uchar_t)(n >> 8); - *bufptr++ = (ipp_uchar_t)n; +/* + * 'ipp_read_http()' - Semi-blocking read on a HTTP connection... + */ - /* Text */ - if (n > 0) - { - memcpy(bufptr, value->string.text, (size_t)n); - bufptr += n; - } - } - break; +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 */ - 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); - } + DEBUG_printf(("7ipp_read_http(http=%p, buffer=%p, length=%d)", (void *)http, (void *)buffer, (int)length)); - bufptr = buffer; - } + /* + * Loop until all bytes are read... + */ - if (i) - { - /* - * Arrays and sets are done by sending additional - * values with a zero-length name... - */ + 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)); - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } + if (http->state == HTTP_STATE_WAITING) + break; - /* - * Write a data length of 0 and flush the buffer... - */ + if (http->used == 0 && !http->blocking) + { + /* + * Wait up to 10 seconds for more data on non-blocking sockets... + */ - *bufptr++ = 0; - *bufptr++ = 0; + if (!httpWait(http, 10000)) + { + /* + * Signal no data... + */ - if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0) - { - DEBUG_puts("1ippWriteIO: Could not write IPP " - "attribute..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + bytes = -1; + break; + } + } + else if (http->used == 0 && http->timeout_value > 0) + { + /* + * Wait up to timeout seconds for more data on blocking sockets... + */ - bufptr = buffer; + if (!httpWait(http, (int)(1000 * http->timeout_value))) + { + /* + * Signal no data... + */ - /* - * Then write the collection attribute... - */ + bytes = -1; + break; + } + } - value->collection->state = IPP_STATE_IDLE; + if ((bytes = httpRead2(http, (char *)buffer, length - (size_t)tbytes)) < 0) + { +#ifdef _WIN32 + break; +#else + if (errno != EAGAIN && errno != EINTR) + 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; + bytes = 0; +#endif /* _WIN32 */ + } + else if (bytes == 0) + 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... - */ + /* + * Return the number of bytes read... + */ - 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 (tbytes == 0 && bytes < 0) + tbytes = -1; - bufptr = buffer; - } + DEBUG_printf(("8ipp_read_http: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST tbytes)); - *bufptr++ = (ipp_uchar_t)attr->value_tag; - *bufptr++ = 0; - *bufptr++ = 0; - } + return (tbytes); +} - /* - * 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; +/* + * 'ipp_read_io()' - Read data for an IPP message. + */ - if (n > (IPP_BUF_SIZE - 2)) - { - DEBUG_printf(("1ippWriteIO: Data length too long (%d)", - n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } +static ipp_state_t /* O - Current state */ +ipp_read_io(void *src, /* I - Data source */ + ipp_iocb_t cb, /* I - Read callback function */ + bool 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 */ - 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; - } + 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)); - /* Length of unknown value */ - *bufptr++ = (ipp_uchar_t)(n >> 8); - *bufptr++ = (ipp_uchar_t)n; + if (depth > 10) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP message nested too deeply."), true); + return (IPP_STATE_ERROR); + } - /* Value */ - if (n > 0) - { - memcpy(bufptr, value->unknown.data, (size_t)n); - bufptr += n; - } - } - break; - } + if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL) + { + DEBUG_puts("1ipp_read_io: Unable to get read buffer."); + return (IPP_STATE_ERROR); + } - /* - * Write the data out... + switch (ipp->state) + { + case IPP_STATE_IDLE : + ipp->state ++; /* Avoid common problem... */ + + case IPP_STATE_HEADER : + if (parent == NULL) + { + /* + * Get the request header... */ - if (bufptr > buffer) + if ((*cb)(src, buffer, 8) < 8) { - 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))); + DEBUG_puts("1ipp_read_io: Unable to read header."); + goto rollback; } /* - * If blocking is disabled and we aren't at the end of the attribute - * list, stop here... + * Then copy the request header over... */ - if (!blocking && ipp->current) - break; - } + 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]; - if (ipp->current == NULL) - { - /* - * Done with all of the attributes; add the end-of-attributes - * tag or end-collection attribute... - */ + 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)); + } - 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; - } + ipp->state = IPP_STATE_ATTRIBUTE; + ipp->current = NULL; + ipp->curtag = IPP_TAG_ZERO; + ipp->prev = ipp->last; - if ((*cb)(dst, buffer, (size_t)n) < 0) + /* + * If blocking is disabled, stop here... + */ + + if (!blocking) + break; + + case IPP_STATE_ATTRIBUTE : + for (;;) + { + if ((*cb)(src, buffer, 1) < 1) { - DEBUG_puts("1ippWriteIO: Could not write IPP end-tag..."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + DEBUG_puts("1ipp_read_io: Callback returned EOF/error"); + goto rollback; } - ipp->state = IPP_STATE_DATA; - } - break; - - case IPP_STATE_DATA : - break; + DEBUG_printf(("2ipp_read_io: ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev)); - default : - break; /* anti-compiler-warning-code */ - } + /* + * Read this attribute... + */ - _cupsBufferRelease((char *)buffer); + tag = (ipp_tag_t)buffer[0]; + if (tag == IPP_TAG_EXTENSION) + { + /* + * Read 32-bit "extension" tag... + */ - return (ipp->state); -} + if ((*cb)(src, buffer, 4) < 4) + { + DEBUG_puts("1ipp_read_io: Callback returned EOF/error"); + goto rollback; + } + tag = (ipp_tag_t)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); -/* - * 'ipp_add_attr()' - Add a new attribute to the message. - */ + if (tag & IPP_TAG_CUPS_CONST) + { + /* + * Fail if the high bit is set in the tag... + */ -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 */ + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP extension tag larger than 0x7FFFFFFF."), 1); + DEBUG_printf(("1ipp_read_io: bad tag 0x%x.", tag)); + goto rollback; + } + } + if (tag == IPP_TAG_END) + { + /* + * No more attributes left... + */ - 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)); + DEBUG_puts("2ipp_read_io: IPP_TAG_END."); - /* - * Range check input... - */ + 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 (!ipp || num_values < 0) - return (NULL); + 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; - /* - * Allocate memory, rounding the allocation up as needed... - */ + 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 (num_values <= 1) - alloc_values = 1; - else - alloc_values = (num_values + IPP_MAX_VALUES - 1) & ~(IPP_MAX_VALUES - 1); + DEBUG_printf(("2ipp_read_io: value tag=%x(%s)", tag, + ippTagString(tag))); - attr = calloc(1, sizeof(ipp_attribute_t) + - (size_t)(alloc_values - 1) * sizeof(_ipp_value_t)); + /* + * Get the name... + */ - if (attr) - { - /* - * Initialize attribute... - */ + if ((*cb)(src, buffer, 2) < 2) + { + DEBUG_puts("1ipp_read_io: unable to read name length."); + goto rollback; + } - DEBUG_printf(("4debug_alloc: %p %s %s%s (%d values)", (void *)attr, name, num_values > 1 ? "1setOf " : "", ippTagString(value_tag), num_values)); + n = (buffer[0] << 8) | buffer[1]; - if (name) - attr->name = _cupsStrAlloc(name); + if (n >= IPP_BUF_SIZE) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP name larger than 32767 bytes."), 1); + DEBUG_printf(("1ipp_read_io: bad name length %d.", n)); + goto rollback; + } - attr->group_tag = group_tag; - attr->value_tag = value_tag; - attr->num_values = num_values; + DEBUG_printf(("2ipp_read_io: name length=%d", n)); - /* - * Add it to the end of the linked list... - */ + 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) + { + /* + * More values for current attribute... + */ - if (ipp->last) - ipp->last->next = attr; - else - ipp->attrs = attr; + 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; + } - ipp->prev = ipp->last; - ipp->last = ipp->current = attr; - } + attr = ipp->current; + value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_CUPS_MASK); - DEBUG_printf(("5ipp_add_attr: Returning %p", (void *)attr)); + /* + * Make sure we aren't adding a new value of a different + * type... + */ - return (attr); -} + 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... + */ -/* - * 'ipp_free_values()' - Free attribute 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; + } -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 (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(("4ipp_free_values(attr=%p, element=%d, count=%d)", (void *)attr, element, count)); + 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; + } - if (!(attr->value_tag & IPP_TAG_CUPS_CONST)) - { - /* - * Free values as needed... - */ + /* + * Finally, reallocate the attribute array 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; + if ((value = ipp_set_value(ipp, &attr, attr->num_values)) == NULL) + 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 ++) + else if (tag == IPP_TAG_MEMBERNAME) { - _cupsStrFree(value->string.text); - value->string.text = NULL; - } - break; + /* + * Name must be length 0! + */ - 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) + { + _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; + } - case IPP_TAG_BEGIN_COLLECTION : - for (i = count, value = attr->values + element; - i > 0; - i --, value ++) - { - ippDelete(value->collection); - value->collection = NULL; - } - break; + if (ipp->current) + ipp->prev = ipp->current; - case IPP_TAG_STRING : - default : - for (i = count, value = attr->values + element; - i > 0; - i --, value ++) - { - if (value->unknown.data) + attr = ipp->current = ipp_add_attr(ipp, NULL, ipp->curtag, IPP_TAG_ZERO, 1); + if (!attr) { - free(value->unknown.data); - value->unknown.data = NULL; + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to allocate IPP attribute."), 1); + DEBUG_puts("1ipp_read_io: unable to allocate attribute."); + goto rollback; } - } - break; - } - } - /* - * If we are not freeing values from the end, move the remaining values up... - */ + DEBUG_printf(("2ipp_read_io: membername, ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev)); - 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)); + value = attr->values; + } + else if (tag != IPP_TAG_END_COLLECTION) + { + /* + * New attribute; read the name and add it... + */ - attr->num_values -= count; -} + if ((*cb)(src, buffer, (size_t)n) < n) + { + DEBUG_puts("1ipp_read_io: unable to read name."); + goto rollback; + } + buffer[n] = '\0'; -/* - * '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. - */ + if (ipp->current) + ipp->prev = ipp->current; -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 ((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("1ipp_read_io: unable to allocate attribute."); + goto rollback; + } + DEBUG_printf(("2ipp_read_io: name=\"%s\", ipp->current=%p, ipp->prev=%p", buffer, (void *)ipp->current, (void *)ipp->prev)); - /* - * Convert values to lowercase and change _ to - as needed... - */ + value = attr->values; + } + else + { + attr = NULL; + value = NULL; + } - for (bufptr = buffer, bufend = buffer + bufsize - 1; - *value && bufptr < bufend; - value ++) - if (*value == '_') - *bufptr++ = '-'; - else - *bufptr++ = (char)_cups_tolower(*value); + if ((*cb)(src, buffer, 2) < 2) + { + DEBUG_puts("1ipp_read_io: unable to read value length."); + goto rollback; + } - *bufptr = '\0'; + n = (buffer[0] << 8) | buffer[1]; + DEBUG_printf(("2ipp_read_io: value length=%d", n)); - /* - * Return the converted string... - */ + 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; + } - return (buffer); -} + 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; + } + if ((*cb)(src, buffer, 4) < 4) + { + DEBUG_puts("1ipp_read_io: Unable to read integer value."); + 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". - */ + n = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; -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 (attr->value_tag == IPP_TAG_RANGE) + value->range.lower = value->range.upper = n; + else + value->integer = n; + break; - if (!_cups_strcasecmp(locale, "c")) - { - strlcpy(buffer, "en", bufsize); - return (buffer); - } - else - return (ipp_get_code(locale, buffer, bufsize)); -} + 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; + } + if ((*cb)(src, buffer, 1) < 1) + { + DEBUG_puts("1ipp_read_io: Unable to read boolean value."); + goto rollback; + } -/* - * 'ipp_length()' - Compute the length of an IPP message or collection value. - */ + value->boolean = (char)buffer[0]; + break; -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 */ + 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; - DEBUG_printf(("3ipp_length(ipp=%p, collection=%d)", (void *)ipp, collection)); + attr->value_tag = IPP_TAG_TEXT; + } - if (!ipp) - { - DEBUG_puts("4ipp_length: Returning 0 bytes"); - return (0); - } + 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; + } + } - /* - * Start with 8 bytes for the IPP message header... - */ + buffer[n] = '\0'; + value->string.text = _cupsStrAlloc((char *)buffer); + DEBUG_printf(("2ipp_read_io: value=\"%s\"", value->string.text)); + break; - bytes = collection ? 0 : 8; + 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; + } - /* - * Then add the lengths of each attribute... - */ + if ((*cb)(src, value->date, 11) < 11) + { + DEBUG_puts("1ipp_read_io: Unable to read date value."); + goto rollback; + } + break; - group = IPP_TAG_ZERO; + 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; + } - 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; + if ((*cb)(src, buffer, 9) < 9) + { + DEBUG_puts("1ipp_read_io: Unable to read resolution value."); + goto rollback; + } - bytes ++; /* Group tag */ - } + 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 (!attr->name) - continue; + 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; + } - 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, 8) < 8) + { + DEBUG_puts("1ipp_read_io: Unable to read range 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->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 (collection) - bytes += 5; /* Add membername overhead */ + 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(("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; + /* + * text-with-language and name-with-language are composite + * values: + * + * language-length + * language + * text-length + * text + */ - 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; + n = (bufptr[0] << 8) | bufptr[1]; - case IPP_TAG_DATE : - bytes += (size_t)(11 * attr->num_values); - 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_RESOLUTION : - bytes += (size_t)(9 * attr->num_values); - break; + memcpy(string, bufptr + 2, (size_t)n); + string[n] = '\0'; - case IPP_TAG_RANGE : - bytes += (size_t)(8 * attr->num_values); - break; + value->string.language = _cupsStrAlloc((char *)string); - case IPP_TAG_TEXTLANG : - case IPP_TAG_NAMELANG : - bytes += (size_t)(4 * attr->num_values); - /* Charset + text length */ + bufptr += 2 + n; + n = (bufptr[0] << 8) | bufptr[1]; - for (i = 0, value = attr->values; - i < attr->num_values; - i ++, value ++) - { - if (value->string.language) - bytes += strlen(value->string.language); + 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; + } - if (value->string.text) - bytes += strlen(value->string.text); - } - break; + bufptr[2 + n] = '\0'; + value->string.text = _cupsStrAlloc((char *)bufptr + 2); + 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; + case IPP_TAG_BEGIN_COLLECTION : + /* + * Oh, boy, here comes a collection value, so read it... + */ - default : - for (i = 0, value = attr->values; - i < attr->num_values; - i ++, value ++) - bytes += (size_t)value->unknown.length; - break; - } - } + value->collection = ippNew(); - /* - * Finally, add 1 byte for the "end of attributes" tag or 5 bytes - * for the "end of collection" tag and return... - */ + 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; + } - if (collection) - bytes += 5; - else - bytes ++; + 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; - DEBUG_printf(("4ipp_length: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST 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; + } - return (bytes); -} + _cupsBufferRelease((char *)buffer); + DEBUG_puts("1ipp_read_io: endCollection tag..."); + return (ipp->state = IPP_STATE_DATA); -/* - * 'ipp_read_http()' - Semi-blocking read on a HTTP connection... - */ + case IPP_TAG_MEMBERNAME : + /* + * The value the name of the member in the collection, which + * we need to carry over... + */ -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 */ + 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); - DEBUG_printf(("7ipp_read_http(http=%p, buffer=%p, length=%d)", (void *)http, (void *)buffer, (int)length)); + /* + * Since collection members are encoded differently than + * regular attributes, make sure we don't start with an + * empty value... + */ - /* - * Loop until all bytes are read... - */ + attr->num_values --; - 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)); + DEBUG_printf(("2ipp_read_io: member name=\"%s\"", attr->name)); + break; - if (http->state == HTTP_STATE_WAITING) - 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; + } - if (http->used == 0 && !http->blocking) - { - /* - * Wait up to 10 seconds for more data on non-blocking sockets... - */ + value->unknown.length = n; - if (!httpWait(http, 10000)) - { - /* - * Signal no data... - */ + 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("1ipp_read_io: Unable to allocate value"); + goto rollback; + } - bytes = -1; - break; - } - } - else if (http->used == 0 && http->timeout_value > 0) - { - /* - * Wait up to timeout seconds for more data on blocking sockets... - */ + 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 (!httpWait(http, (int)(1000 * http->timeout_value))) - { - /* - * Signal no data... - */ + /* + * If blocking is disabled, stop here... + */ - bytes = -1; - break; - } - } + 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); }