mirror of
https://github.com/MopriaAlliance/CUPS-for-Android.git
synced 2025-08-03 19:04:37 +08:00
5575 lines
145 KiB
C
5575 lines
145 KiB
C
/*
|
|
* "$Id: ipp.c 10102 2011-11-02 23:52:39Z mike $"
|
|
*
|
|
* Internet Printing Protocol functions for CUPS.
|
|
*
|
|
* Copyright 2007-2013 by Apple Inc.
|
|
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
|
|
*
|
|
* These coded instructions, statements, and computer programs are the
|
|
* property of Apple Inc. and are protected by Federal copyright
|
|
* law. Distribution and use rights are outlined in the file "LICENSE.txt"
|
|
* which should have been included with this file. If this file is
|
|
* file is missing or damaged, see the license at "http://www.cups.org/".
|
|
*
|
|
* This file is subject to the Apple OS-Developed Software exception.
|
|
*
|
|
* Contents:
|
|
*
|
|
* _cupsBufferGet() - Get a read/write buffer.
|
|
* _cupsBufferRelease() - Release a read/write buffer.
|
|
* ippAddBoolean() - Add a boolean attribute to an IPP message.
|
|
* ippAddBooleans() - Add an array of boolean values.
|
|
* ippAddCollection() - Add a collection value.
|
|
* ippAddCollections() - Add an array of collection values.
|
|
* ippAddDate() - Add a date attribute to an IPP message.
|
|
* ippAddInteger() - Add a integer attribute to an IPP message.
|
|
* ippAddIntegers() - Add an array of integer values.
|
|
* ippAddOctetString() - Add an octetString value to an IPP message.
|
|
* ippAddOutOfBand() - Add an out-of-band value to an IPP message.
|
|
* ippAddRange() - Add a range of values to an IPP message.
|
|
* ippAddRanges() - Add ranges of values to an IPP message.
|
|
* ippAddResolution() - Add a resolution value to an IPP message.
|
|
* ippAddResolutions() - Add resolution values to an IPP message.
|
|
* ippAddSeparator() - Add a group separator to an IPP message.
|
|
* ippAddString() - Add a language-encoded string to an IPP message.
|
|
* ippAddStrings() - Add language-encoded strings to an IPP message.
|
|
* ippCopyAttribute() - Copy an attribute.
|
|
* ippCopyAttributes() - Copy attributes from one IPP message to another.
|
|
* ippDateToTime() - Convert from RFC 1903 Date/Time format to UNIX
|
|
* time in seconds.
|
|
* ippDelete() - Delete an IPP message.
|
|
* ippDeleteAttribute() - Delete a single attribute in an IPP message.
|
|
* ippDeleteValues() - Delete values in an attribute.
|
|
* ippFindAttribute() - Find a named attribute in a request.
|
|
* ippFindNextAttribute() - Find the next named attribute in a request.
|
|
* ippFirstAttribute() - Return the first attribute in the message.
|
|
* ippGetBoolean() - Get a boolean value for an attribute.
|
|
* ippGetCollection() - Get a collection value for an attribute.
|
|
* ippGetCount() - Get the number of values in an attribute.
|
|
* ippGetDate() - Get a date value for an attribute.
|
|
* ippGetGroupTag() - Get the group associated with an attribute.
|
|
* ippGetInteger() - Get the integer/enum value for an attribute.
|
|
* ippGetName() - Get the attribute name.
|
|
* ippGetOperation() - Get the operation ID in an IPP message.
|
|
* ippGetRange() - Get a rangeOfInteger value from an attribute.
|
|
* ippGetRequestId() - Get the request ID from an IPP message.
|
|
* ippGetResolution() - Get a resolution value for an attribute.
|
|
* ippGetStatusCode() - Get the status code from an IPP response or event
|
|
* message.
|
|
* ippGetString() - Get the string and optionally the language code
|
|
* for an attribute.
|
|
* ippGetValueTag() - Get the value tag for an attribute.
|
|
* ippGetVersion() - Get the major and minor version number from an
|
|
* IPP message.
|
|
* ippLength() - Compute the length of an IPP message.
|
|
* ippNextAttribute() - Return the next attribute in the message.
|
|
* ippNew() - Allocate a new IPP message.
|
|
* ippNewRequest() - Allocate a new IPP request message.
|
|
* ippRead() - Read data for an IPP message from a HTTP
|
|
* connection.
|
|
* ippReadFile() - Read data for an IPP message from a file.
|
|
* ippReadIO() - Read data for an IPP message.
|
|
* ippSetBoolean() - Set a boolean value in an attribute.
|
|
* ippSetCollection() - Set a collection value in an attribute.
|
|
* ippSetDate() - Set a date value in an attribute.
|
|
* ippSetGroupTag() - Set the group tag of an attribute.
|
|
* ippSetInteger() - Set an integer or enum value in an attribute.
|
|
* ippSetName() - Set the name of an attribute.
|
|
* ippSetOperation() - Set the operation ID in an IPP request message.
|
|
* ippSetRange() - Set a rangeOfInteger value in an attribute.
|
|
* ippSetRequestId() - Set the request ID in an IPP message.
|
|
* ippSetResolution() - Set a resolution value in an attribute.
|
|
* ippSetState() - Set the current state of the IPP message.
|
|
* ippSetStatusCode() - Set the status code in an IPP response or event
|
|
* message.
|
|
* ippSetString() - Set a string value in an attribute.
|
|
* ippSetValueTag() - Set the value tag of an attribute.
|
|
* ippSetVersion() - Set the version number in an IPP message.
|
|
* ippTimeToDate() - Convert from UNIX time to RFC 1903 format.
|
|
* ippWrite() - Write data for an IPP message to a HTTP
|
|
* connection.
|
|
* ippWriteFile() - Write data for an IPP message to a file.
|
|
* ippWriteIO() - Write data for an IPP message.
|
|
* ipp_add_attr() - Add a new attribute to the message.
|
|
* ipp_free_values() - Free attribute values.
|
|
* ipp_get_code() - Convert a C locale/charset name into an IPP
|
|
* language/charset code.
|
|
* ipp_lang_code() - Convert a C locale name into an IPP language
|
|
* code.
|
|
* ipp_length() - Compute the length of an IPP message or
|
|
* collection value.
|
|
* ipp_read_http() - Semi-blocking read on a HTTP connection...
|
|
* ipp_read_file() - Read IPP data from a file.
|
|
* ipp_set_value() - Get the value element from an attribute,
|
|
* expanding it as needed.
|
|
* ipp_write_file() - Write IPP data to a file.
|
|
*/
|
|
|
|
/*
|
|
* Include necessary headers...
|
|
*/
|
|
|
|
#include "cups-private.h"
|
|
#ifdef WIN32
|
|
# include <io.h>
|
|
#endif /* WIN32 */
|
|
|
|
|
|
/*
|
|
* Local functions...
|
|
*/
|
|
|
|
static ipp_attribute_t *ipp_add_attr(ipp_t *ipp, const char *name,
|
|
ipp_tag_t group_tag, ipp_tag_t value_tag,
|
|
int num_values);
|
|
static void ipp_free_values(ipp_attribute_t *attr, int element,
|
|
int count);
|
|
static char *ipp_get_code(const char *locale, char *buffer,
|
|
size_t bufsize)
|
|
__attribute__((nonnull(1,2)));
|
|
static char *ipp_lang_code(const char *locale, char *buffer,
|
|
size_t bufsize)
|
|
__attribute__((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 _ipp_value_t *ipp_set_value(ipp_t *ipp, ipp_attribute_t **attr,
|
|
int element);
|
|
static ssize_t ipp_write_file(int *fd, ipp_uchar_t *buffer,
|
|
size_t length);
|
|
|
|
|
|
/*
|
|
* '_cupsBufferGet()' - Get a read/write buffer.
|
|
*/
|
|
|
|
char * /* O - Buffer */
|
|
_cupsBufferGet(size_t size) /* I - Size required */
|
|
{
|
|
_cups_buffer_t *buffer; /* Current buffer */
|
|
_cups_globals_t *cg = _cupsGlobals();
|
|
/* Global data */
|
|
|
|
|
|
for (buffer = cg->cups_buffers; buffer; buffer = buffer->next)
|
|
if (!buffer->used && buffer->size >= size)
|
|
break;
|
|
|
|
if (!buffer)
|
|
{
|
|
if ((buffer = malloc(sizeof(_cups_buffer_t) + size - 1)) == NULL)
|
|
return (NULL);
|
|
|
|
buffer->next = cg->cups_buffers;
|
|
buffer->size = size;
|
|
cg->cups_buffers = buffer;
|
|
}
|
|
|
|
buffer->used = 1;
|
|
|
|
return (buffer->d);
|
|
}
|
|
|
|
|
|
/*
|
|
* '_cupsBufferRelease()' - Release a read/write buffer.
|
|
*/
|
|
|
|
void
|
|
_cupsBufferRelease(char *b) /* I - Buffer to release */
|
|
{
|
|
_cups_buffer_t *buffer; /* Buffer */
|
|
|
|
|
|
/*
|
|
* Mark this buffer as unused...
|
|
*/
|
|
|
|
buffer = (_cups_buffer_t *)(b - offsetof(_cups_buffer_t, d));
|
|
buffer->used = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddBoolean()' - Add a boolean attribute to an IPP message.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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@).
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddBoolean(ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
const char *name, /* I - Name of attribute */
|
|
char value) /* I - Value of attribute */
|
|
{
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
|
|
|
|
DEBUG_printf(("ippAddBoolean(ipp=%p, group=%02x(%s), name=\"%s\", value=%d)",
|
|
ipp, group, ippTagString(group), name, value));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BOOLEAN, 1)) == NULL)
|
|
return (NULL);
|
|
|
|
attr->values[0].boolean = value;
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddBooleans()' - Add an array of boolean values.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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@).
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddBooleans(ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
const char *name, /* I - Name of attribute */
|
|
int num_values, /* I - Number of values */
|
|
const char *values) /* I - Values */
|
|
{
|
|
int i; /* Looping var */
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
_ipp_value_t *value; /* Current value */
|
|
|
|
|
|
DEBUG_printf(("ippAddBooleans(ipp=%p, group=%02x(%s), name=\"%s\", "
|
|
"num_values=%d, values=%p)", ipp, group, ippTagString(group),
|
|
name, num_values, values));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
|
|
num_values < 1)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BOOLEAN, num_values)) == NULL)
|
|
return (NULL);
|
|
|
|
if (values)
|
|
{
|
|
for (i = num_values, value = attr->values;
|
|
i > 0;
|
|
i --, value ++)
|
|
value->boolean = *values++;
|
|
}
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddCollection()' - Add a collection value.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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.1.19/OS X 10.3@
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddCollection(ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
const char *name, /* I - Name of attribute */
|
|
ipp_t *value) /* I - Value */
|
|
{
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
|
|
|
|
DEBUG_printf(("ippAddCollection(ipp=%p, group=%02x(%s), name=\"%s\", "
|
|
"value=%p)", ipp, group, ippTagString(group), name, value));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BEGIN_COLLECTION, 1)) == NULL)
|
|
return (NULL);
|
|
|
|
attr->values[0].collection = value;
|
|
|
|
if (value)
|
|
value->use ++;
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddCollections()' - Add an array of collection values.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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.1.19/OS X 10.3@
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddCollections(
|
|
ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
const char *name, /* I - Name of attribute */
|
|
int num_values, /* I - Number of values */
|
|
const ipp_t **values) /* I - Values */
|
|
{
|
|
int i; /* Looping var */
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
_ipp_value_t *value; /* Current value */
|
|
|
|
|
|
DEBUG_printf(("ippAddCollections(ipp=%p, group=%02x(%s), name=\"%s\", "
|
|
"num_values=%d, values=%p)", ipp, group, ippTagString(group),
|
|
name, num_values, values));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
|
|
num_values < 1)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BEGIN_COLLECTION,
|
|
num_values)) == NULL)
|
|
return (NULL);
|
|
|
|
if (values)
|
|
{
|
|
for (i = num_values, value = attr->values;
|
|
i > 0;
|
|
i --, value ++)
|
|
{
|
|
value->collection = (ipp_t *)*values++;
|
|
value->collection->use ++;
|
|
}
|
|
}
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddDate()' - Add a date attribute to an IPP message.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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@).
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddDate(ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
const char *name, /* I - Name of attribute */
|
|
const ipp_uchar_t *value) /* I - Value */
|
|
{
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
|
|
|
|
DEBUG_printf(("ippAddDate(ipp=%p, group=%02x(%s), name=\"%s\", value=%p)",
|
|
ipp, group, ippTagString(group), name, value));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !name || !value || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_DATE, 1)) == NULL)
|
|
return (NULL);
|
|
|
|
memcpy(attr->values[0].date, value, 11);
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddInteger()' - Add a integer attribute to an IPP message.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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@).
|
|
*
|
|
* Supported values include enum (@code IPP_TAG_ENUM@) and integer
|
|
* (@code IPP_TAG_INTEGER@).
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddInteger(ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
ipp_tag_t value_tag, /* I - Type of attribute */
|
|
const char *name, /* I - Name of attribute */
|
|
int value) /* I - Value of attribute */
|
|
{
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
|
|
|
|
DEBUG_printf(("ippAddInteger(ipp=%p, group=%02x(%s), type=%02x(%s), "
|
|
"name=\"%s\", value=%d)", ipp, group, ippTagString(group),
|
|
value_tag, ippTagString(value_tag), name, value));
|
|
|
|
value_tag &= IPP_TAG_MASK;
|
|
|
|
/*
|
|
* Special-case for legacy usage: map out-of-band attributes to new ippAddOutOfBand
|
|
* function...
|
|
*/
|
|
|
|
if (value_tag >= IPP_TAG_UNSUPPORTED_VALUE && value_tag <= IPP_TAG_ADMINDEFINE)
|
|
return (ippAddOutOfBand(ipp, group, value_tag, name));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
#if 0
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
|
|
(value_tag != IPP_TAG_INTEGER && value_tag != IPP_TAG_ENUM))
|
|
return (NULL);
|
|
#else
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
|
|
return (NULL);
|
|
#endif /* 0 */
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
if ((attr = ipp_add_attr(ipp, name, group, value_tag, 1)) == NULL)
|
|
return (NULL);
|
|
|
|
attr->values[0].integer = value;
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddIntegers()' - Add an array of integer values.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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@).
|
|
*
|
|
* Supported values include enum (@code IPP_TAG_ENUM@) and integer
|
|
* (@code IPP_TAG_INTEGER@).
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddIntegers(ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
ipp_tag_t value_tag, /* I - Type of attribute */
|
|
const char *name, /* I - Name of attribute */
|
|
int num_values, /* I - Number of values */
|
|
const int *values) /* I - Values */
|
|
{
|
|
int i; /* Looping var */
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
_ipp_value_t *value; /* Current value */
|
|
|
|
|
|
DEBUG_printf(("ippAddIntegers(ipp=%p, group=%02x(%s), type=%02x(%s), "
|
|
"name=\"%s\", num_values=%d, values=%p)", ipp,
|
|
group, ippTagString(group), value_tag, ippTagString(value_tag), name,
|
|
num_values, values));
|
|
|
|
value_tag &= IPP_TAG_MASK;
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
#if 0
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
|
|
(value_tag != IPP_TAG_INTEGER && value_tag != IPP_TAG_ENUM) ||
|
|
num_values < 1)
|
|
return (NULL);
|
|
#else
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
|
|
num_values < 1)
|
|
return (NULL);
|
|
#endif /* 0 */
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
if ((attr = ipp_add_attr(ipp, name, group, value_tag, num_values)) == NULL)
|
|
return (NULL);
|
|
|
|
if (values)
|
|
{
|
|
for (i = num_values, value = attr->values;
|
|
i > 0;
|
|
i --, value ++)
|
|
value->integer = *values++;
|
|
}
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddOctetString()' - Add an octetString value to an IPP message.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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.2/OS X 10.5@
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddOctetString(ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
const char *name, /* I - Name of attribute */
|
|
const void *data, /* I - octetString data */
|
|
int datalen) /* I - Length of data in bytes */
|
|
{
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
|
|
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
|
|
return (NULL);
|
|
|
|
if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_STRING, 1)) == NULL)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Initialize the attribute data...
|
|
*/
|
|
|
|
attr->values[0].unknown.length = datalen;
|
|
|
|
if (data)
|
|
{
|
|
if ((attr->values[0].unknown.data = malloc(datalen)) == NULL)
|
|
{
|
|
ippDeleteAttribute(ipp, attr);
|
|
return (NULL);
|
|
}
|
|
|
|
memcpy(attr->values[0].unknown.data, data, datalen);
|
|
}
|
|
|
|
/*
|
|
* Return the new attribute...
|
|
*/
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddOutOfBand()' - Add an out-of-band value to an IPP message.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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@).
|
|
*
|
|
* Supported out-of-band values include unsupported-value
|
|
* (@code IPP_TAG_UNSUPPORTED_VALUE@), default (@code IPP_TAG_DEFAULT@), unknown
|
|
* (@code IPP_TAG_UNKNOWN@), no-value (@code IPP_TAG_NOVALUE@), not-settable
|
|
* (@code IPP_TAG_NOTSETTABLE@), delete-attribute (@code IPP_TAG_DELETEATTR@), and
|
|
* admin-define (@code IPP_TAG_ADMINDEFINE@).
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddOutOfBand(ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
ipp_tag_t value_tag, /* I - Type of attribute */
|
|
const char *name) /* I - Name of attribute */
|
|
{
|
|
DEBUG_printf(("ippAddOutOfBand(ipp=%p, group=%02x(%s), value_tag=%02x(%s), "
|
|
"name=\"%s\")", ipp, group, ippTagString(group), value_tag,
|
|
ippTagString(value_tag), name));
|
|
|
|
value_tag &= IPP_TAG_MASK;
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
|
|
(value_tag != IPP_TAG_UNSUPPORTED_VALUE &&
|
|
value_tag != IPP_TAG_DEFAULT &&
|
|
value_tag != IPP_TAG_UNKNOWN &&
|
|
value_tag != IPP_TAG_NOVALUE &&
|
|
value_tag != IPP_TAG_NOTSETTABLE &&
|
|
value_tag != IPP_TAG_DELETEATTR &&
|
|
value_tag != IPP_TAG_ADMINDEFINE))
|
|
return (NULL);
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
return (ipp_add_attr(ipp, name, group, value_tag, 1));
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddRange()' - Add a range of values to an IPP message.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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@).
|
|
*
|
|
* The @code lower@ parameter must be less than or equal to the @code upper@ parameter.
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddRange(ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
const char *name, /* I - Name of attribute */
|
|
int lower, /* I - Lower value */
|
|
int upper) /* I - Upper value */
|
|
{
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
|
|
|
|
DEBUG_printf(("ippAddRange(ipp=%p, group=%02x(%s), name=\"%s\", lower=%d, "
|
|
"upper=%d)", ipp, group, ippTagString(group), name, lower,
|
|
upper));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RANGE, 1)) == NULL)
|
|
return (NULL);
|
|
|
|
attr->values[0].range.lower = lower;
|
|
attr->values[0].range.upper = upper;
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddRanges()' - Add ranges of values to an IPP message.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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@).
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddRanges(ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
const char *name, /* I - Name of attribute */
|
|
int num_values, /* I - Number of values */
|
|
const int *lower, /* I - Lower values */
|
|
const int *upper) /* I - Upper values */
|
|
{
|
|
int i; /* Looping var */
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
_ipp_value_t *value; /* Current value */
|
|
|
|
|
|
DEBUG_printf(("ippAddRanges(ipp=%p, group=%02x(%s), name=\"%s\", "
|
|
"num_values=%d, lower=%p, upper=%p)", ipp, group,
|
|
ippTagString(group), name, num_values, lower, upper));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
|
|
num_values < 1)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RANGE, num_values)) == NULL)
|
|
return (NULL);
|
|
|
|
if (lower && upper)
|
|
{
|
|
for (i = num_values, value = attr->values;
|
|
i > 0;
|
|
i --, value ++)
|
|
{
|
|
value->range.lower = *lower++;
|
|
value->range.upper = *upper++;
|
|
}
|
|
}
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddResolution()' - Add a resolution value to an IPP message.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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@).
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddResolution(ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
const char *name, /* I - Name of attribute */
|
|
ipp_res_t units, /* I - Units for resolution */
|
|
int xres, /* I - X resolution */
|
|
int yres) /* I - Y resolution */
|
|
{
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
|
|
|
|
DEBUG_printf(("ippAddResolution(ipp=%p, group=%02x(%s), name=\"%s\", "
|
|
"units=%d, xres=%d, yres=%d)", ipp, group,
|
|
ippTagString(group), name, units, xres, yres));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
|
|
units < IPP_RES_PER_INCH || units > IPP_RES_PER_CM ||
|
|
xres < 0 || yres < 0)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RESOLUTION, 1)) == NULL)
|
|
return (NULL);
|
|
|
|
attr->values[0].resolution.xres = xres;
|
|
attr->values[0].resolution.yres = yres;
|
|
attr->values[0].resolution.units = units;
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddResolutions()' - Add resolution values to an IPP message.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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@).
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddResolutions(ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
const char *name, /* I - Name of attribute */
|
|
int num_values,/* I - Number of values */
|
|
ipp_res_t units, /* I - Units for resolution */
|
|
const int *xres, /* I - X resolutions */
|
|
const int *yres) /* I - Y resolutions */
|
|
{
|
|
int i; /* Looping var */
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
_ipp_value_t *value; /* Current value */
|
|
|
|
|
|
DEBUG_printf(("ippAddResolutions(ipp=%p, group=%02x(%s), name=\"%s\", "
|
|
"num_value=%d, units=%d, xres=%p, yres=%p)", ipp, group,
|
|
ippTagString(group), name, num_values, units, xres, yres));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
|
|
num_values < 1 ||
|
|
units < IPP_RES_PER_INCH || units > IPP_RES_PER_CM)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RESOLUTION, num_values)) == NULL)
|
|
return (NULL);
|
|
|
|
if (xres && yres)
|
|
{
|
|
for (i = num_values, value = attr->values;
|
|
i > 0;
|
|
i --, value ++)
|
|
{
|
|
value->resolution.xres = *xres++;
|
|
value->resolution.yres = *yres++;
|
|
value->resolution.units = units;
|
|
}
|
|
}
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddSeparator()' - Add a group separator to an IPP message.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddSeparator(ipp_t *ipp) /* I - IPP message */
|
|
{
|
|
DEBUG_printf(("ippAddSeparator(ipp=%p)", ipp));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
return (ipp_add_attr(ipp, NULL, IPP_TAG_ZERO, IPP_TAG_ZERO, 0));
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddString()' - Add a language-encoded string to an IPP message.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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@).
|
|
*
|
|
* Supported string values include charset (@code IPP_TAG_CHARSET@), keyword
|
|
* (@code IPP_TAG_KEYWORD@), language (@code IPP_TAG_LANGUAGE@), mimeMediaType
|
|
* (@code IPP_TAG_MIMETYPE@), name (@code IPP_TAG_NAME@), nameWithLanguage
|
|
* (@code IPP_TAG_NAMELANG), text (@code IPP_TAG_TEXT@), textWithLanguage
|
|
* (@code IPP_TAG_TEXTLANG@), uri (@code IPP_TAG_URI@), and uriScheme
|
|
* (@code IPP_TAG_URISCHEME@).
|
|
*
|
|
* The @code language@ parameter must be non-@code NULL@ for nameWithLanguage and
|
|
* textWithLanguage string values and must be @code NULL@ for all other string values.
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddString(ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
ipp_tag_t value_tag, /* I - Type of attribute */
|
|
const char *name, /* I - Name of attribute */
|
|
const char *language, /* I - Language code */
|
|
const char *value) /* I - Value */
|
|
{
|
|
ipp_tag_t temp_tag; /* Temporary value tag (masked) */
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
char code[32]; /* Charset/language code buffer */
|
|
|
|
|
|
DEBUG_printf(("ippAddString(ipp=%p, group=%02x(%s), value_tag=%02x(%s), "
|
|
"name=\"%s\", language=\"%s\", value=\"%s\")", ipp,
|
|
group, ippTagString(group), value_tag, ippTagString(value_tag), name,
|
|
language, value));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
temp_tag = (ipp_tag_t)((int)value_tag & IPP_TAG_MASK);
|
|
|
|
#if 0
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
|
|
(temp_tag < IPP_TAG_TEXT && temp_tag != IPP_TAG_TEXTLANG &&
|
|
temp_tag != IPP_TAG_NAMELANG) || temp_tag > IPP_TAG_MIMETYPE)
|
|
return (NULL);
|
|
|
|
if ((temp_tag == IPP_TAG_TEXTLANG || temp_tag == IPP_TAG_NAMELANG)
|
|
!= (language != NULL))
|
|
return (NULL);
|
|
#else
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
|
|
return (NULL);
|
|
#endif /* 0 */
|
|
|
|
/*
|
|
* See if we need to map charset, language, or locale values...
|
|
*/
|
|
|
|
if (language && ((int)value_tag & IPP_TAG_COPY) &&
|
|
strcmp(language, ipp_lang_code(language, code, sizeof(code))))
|
|
value_tag = temp_tag; /* Don't do a fast copy */
|
|
else if (value && value_tag == (ipp_tag_t)(IPP_TAG_CHARSET | IPP_TAG_COPY) &&
|
|
strcmp(value, ipp_get_code(value, code, sizeof(code))))
|
|
value_tag = temp_tag; /* Don't do a fast copy */
|
|
else if (value && value_tag == (ipp_tag_t)(IPP_TAG_LANGUAGE | IPP_TAG_COPY) &&
|
|
strcmp(value, ipp_lang_code(value, code, sizeof(code))))
|
|
value_tag = temp_tag; /* Don't do a fast copy */
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
if ((attr = ipp_add_attr(ipp, name, group, value_tag, 1)) == NULL)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Initialize the attribute data...
|
|
*/
|
|
|
|
if ((int)value_tag & IPP_TAG_COPY)
|
|
{
|
|
attr->values[0].string.language = (char *)language;
|
|
attr->values[0].string.text = (char *)value;
|
|
}
|
|
else
|
|
{
|
|
if (language)
|
|
attr->values[0].string.language = _cupsStrAlloc(ipp_lang_code(language, code,
|
|
sizeof(code)));
|
|
|
|
if (value)
|
|
{
|
|
if (value_tag == IPP_TAG_CHARSET)
|
|
attr->values[0].string.text = _cupsStrAlloc(ipp_get_code(value, code,
|
|
sizeof(code)));
|
|
else if (value_tag == IPP_TAG_LANGUAGE)
|
|
attr->values[0].string.text = _cupsStrAlloc(ipp_lang_code(value, code,
|
|
sizeof(code)));
|
|
else
|
|
attr->values[0].string.text = _cupsStrAlloc(value);
|
|
}
|
|
}
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippAddStrings()' - Add language-encoded strings to an IPP message.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* 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@).
|
|
*
|
|
* Supported string values include charset (@code IPP_TAG_CHARSET@), keyword
|
|
* (@code IPP_TAG_KEYWORD@), language (@code IPP_TAG_LANGUAGE@), mimeMediaType
|
|
* (@code IPP_TAG_MIMETYPE@), name (@code IPP_TAG_NAME@), nameWithLanguage
|
|
* (@code IPP_TAG_NAMELANG), text (@code IPP_TAG_TEXT@), textWithLanguage
|
|
* (@code IPP_TAG_TEXTLANG@), uri (@code IPP_TAG_URI@), and uriScheme
|
|
* (@code IPP_TAG_URISCHEME@).
|
|
*
|
|
* The @code language@ parameter must be non-@code NULL@ for nameWithLanguage and
|
|
* textWithLanguage string values and must be @code NULL@ for all other string values.
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippAddStrings(
|
|
ipp_t *ipp, /* I - IPP message */
|
|
ipp_tag_t group, /* I - IPP group */
|
|
ipp_tag_t value_tag, /* I - Type of attribute */
|
|
const char *name, /* I - Name of attribute */
|
|
int num_values, /* I - Number of values */
|
|
const char *language, /* I - Language code (@code NULL@ for default) */
|
|
const char * const *values) /* I - Values */
|
|
{
|
|
int i; /* Looping var */
|
|
ipp_tag_t temp_tag; /* Temporary value tag (masked) */
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
_ipp_value_t *value; /* Current value */
|
|
char code[32]; /* Language/charset value buffer */
|
|
|
|
|
|
DEBUG_printf(("ippAddStrings(ipp=%p, group=%02x(%s), value_tag=%02x(%s), "
|
|
"name=\"%s\", num_values=%d, language=\"%s\", values=%p)", ipp,
|
|
group, ippTagString(group), value_tag, ippTagString(value_tag), name,
|
|
num_values, language, values));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
temp_tag = (ipp_tag_t)((int)value_tag & IPP_TAG_MASK);
|
|
|
|
#if 0
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
|
|
(temp_tag < IPP_TAG_TEXT && temp_tag != IPP_TAG_TEXTLANG &&
|
|
temp_tag != IPP_TAG_NAMELANG) || temp_tag > IPP_TAG_MIMETYPE ||
|
|
num_values < 1)
|
|
return (NULL);
|
|
|
|
if ((temp_tag == IPP_TAG_TEXTLANG || temp_tag == IPP_TAG_NAMELANG)
|
|
!= (language != NULL))
|
|
return (NULL);
|
|
#else
|
|
if (!ipp || !name || group < IPP_TAG_ZERO ||
|
|
group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
|
|
num_values < 1)
|
|
return (NULL);
|
|
#endif /* 0 */
|
|
|
|
/*
|
|
* See if we need to map charset, language, or locale values...
|
|
*/
|
|
|
|
if (language && ((int)value_tag & IPP_TAG_COPY) &&
|
|
strcmp(language, ipp_lang_code(language, code, sizeof(code))))
|
|
value_tag = temp_tag; /* Don't do a fast copy */
|
|
else if (values && value_tag == (ipp_tag_t)(IPP_TAG_CHARSET | IPP_TAG_COPY))
|
|
{
|
|
for (i = 0; i < num_values; i ++)
|
|
if (strcmp(values[i], ipp_get_code(values[i], code, sizeof(code))))
|
|
{
|
|
value_tag = temp_tag; /* Don't do a fast copy */
|
|
break;
|
|
}
|
|
}
|
|
else if (values && value_tag == (ipp_tag_t)(IPP_TAG_LANGUAGE | IPP_TAG_COPY))
|
|
{
|
|
for (i = 0; i < num_values; i ++)
|
|
if (strcmp(values[i], ipp_lang_code(values[i], code, sizeof(code))))
|
|
{
|
|
value_tag = temp_tag; /* Don't do a fast copy */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create the attribute...
|
|
*/
|
|
|
|
if ((attr = ipp_add_attr(ipp, name, group, value_tag, num_values)) == NULL)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Initialize the attribute data...
|
|
*/
|
|
|
|
for (i = num_values, value = attr->values;
|
|
i > 0;
|
|
i --, value ++)
|
|
{
|
|
if (language)
|
|
{
|
|
if (value == attr->values)
|
|
{
|
|
if ((int)value_tag & IPP_TAG_COPY)
|
|
value->string.language = (char *)language;
|
|
else
|
|
value->string.language = _cupsStrAlloc(ipp_lang_code(language, code,
|
|
sizeof(code)));
|
|
}
|
|
else
|
|
value->string.language = attr->values[0].string.language;
|
|
}
|
|
|
|
if (values)
|
|
{
|
|
if ((int)value_tag & IPP_TAG_COPY)
|
|
value->string.text = (char *)*values++;
|
|
else if (value_tag == IPP_TAG_CHARSET)
|
|
value->string.text = _cupsStrAlloc(ipp_get_code(*values++, code, sizeof(code)));
|
|
else if (value_tag == IPP_TAG_LANGUAGE)
|
|
value->string.text = _cupsStrAlloc(ipp_lang_code(*values++, code, sizeof(code)));
|
|
else
|
|
value->string.text = _cupsStrAlloc(*values++);
|
|
}
|
|
}
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippCopyAttribute()' - Copy an attribute.
|
|
*
|
|
* The specified attribute, @code attr@, is copied to the destination IPP message.
|
|
* When @code quickcopy@ is non-zero, a "shallow" reference copy of the attribute is
|
|
* created - this should only be done as long as the original source IPP message will
|
|
* not be freed for the life of the destination.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
|
|
ipp_attribute_t * /* O - New attribute */
|
|
ippCopyAttribute(
|
|
ipp_t *dst, /* I - Destination IPP message */
|
|
ipp_attribute_t *srcattr, /* I - Attribute to copy */
|
|
int quickcopy) /* I - 1 for a referenced copy, 0 for normal */
|
|
{
|
|
int i; /* Looping var */
|
|
ipp_attribute_t *dstattr; /* Destination attribute */
|
|
_ipp_value_t *srcval, /* Source value */
|
|
*dstval; /* Destination value */
|
|
|
|
|
|
DEBUG_printf(("ippCopyAttribute(dst=%p, srcattr=%p, quickcopy=%d)", dst, srcattr,
|
|
quickcopy));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!dst || !srcattr)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Copy it...
|
|
*/
|
|
|
|
quickcopy = quickcopy ? IPP_TAG_COPY : 0;
|
|
|
|
switch (srcattr->value_tag & ~IPP_TAG_COPY)
|
|
{
|
|
case IPP_TAG_ZERO :
|
|
dstattr = ippAddSeparator(dst);
|
|
break;
|
|
|
|
case IPP_TAG_INTEGER :
|
|
case IPP_TAG_ENUM :
|
|
dstattr = ippAddIntegers(dst, srcattr->group_tag, srcattr->value_tag,
|
|
srcattr->name, srcattr->num_values, NULL);
|
|
if (!dstattr)
|
|
break;
|
|
|
|
for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
|
|
i > 0;
|
|
i --, srcval ++, dstval ++)
|
|
dstval->integer = srcval->integer;
|
|
break;
|
|
|
|
case IPP_TAG_BOOLEAN :
|
|
dstattr = ippAddBooleans(dst, srcattr->group_tag, srcattr->name,
|
|
srcattr->num_values, NULL);
|
|
if (!dstattr)
|
|
break;
|
|
|
|
for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
|
|
i > 0;
|
|
i --, srcval ++, dstval ++)
|
|
dstval->boolean = srcval->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 :
|
|
dstattr = ippAddStrings(dst, srcattr->group_tag,
|
|
(ipp_tag_t)(srcattr->value_tag | quickcopy),
|
|
srcattr->name, srcattr->num_values, NULL, NULL);
|
|
if (!dstattr)
|
|
break;
|
|
|
|
if (quickcopy)
|
|
{
|
|
for (i = srcattr->num_values, srcval = srcattr->values,
|
|
dstval = dstattr->values;
|
|
i > 0;
|
|
i --, srcval ++, dstval ++)
|
|
dstval->string.text = srcval->string.text;
|
|
}
|
|
else if (srcattr->value_tag & IPP_TAG_COPY)
|
|
{
|
|
for (i = srcattr->num_values, srcval = srcattr->values,
|
|
dstval = dstattr->values;
|
|
i > 0;
|
|
i --, srcval ++, dstval ++)
|
|
dstval->string.text = _cupsStrAlloc(srcval->string.text);
|
|
}
|
|
else
|
|
{
|
|
for (i = srcattr->num_values, srcval = srcattr->values,
|
|
dstval = dstattr->values;
|
|
i > 0;
|
|
i --, srcval ++, dstval ++)
|
|
dstval->string.text = _cupsStrRetain(srcval->string.text);
|
|
}
|
|
break;
|
|
|
|
case IPP_TAG_DATE :
|
|
if (srcattr->num_values != 1)
|
|
return (NULL);
|
|
|
|
dstattr = ippAddDate(dst, srcattr->group_tag, srcattr->name,
|
|
srcattr->values[0].date);
|
|
break;
|
|
|
|
case IPP_TAG_RESOLUTION :
|
|
dstattr = ippAddResolutions(dst, srcattr->group_tag, srcattr->name,
|
|
srcattr->num_values, IPP_RES_PER_INCH,
|
|
NULL, NULL);
|
|
if (!dstattr)
|
|
break;
|
|
|
|
for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
|
|
i > 0;
|
|
i --, srcval ++, dstval ++)
|
|
{
|
|
dstval->resolution.xres = srcval->resolution.xres;
|
|
dstval->resolution.yres = srcval->resolution.yres;
|
|
dstval->resolution.units = srcval->resolution.units;
|
|
}
|
|
break;
|
|
|
|
case IPP_TAG_RANGE :
|
|
dstattr = ippAddRanges(dst, srcattr->group_tag, srcattr->name,
|
|
srcattr->num_values, NULL, NULL);
|
|
if (!dstattr)
|
|
break;
|
|
|
|
for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
|
|
i > 0;
|
|
i --, srcval ++, dstval ++)
|
|
{
|
|
dstval->range.lower = srcval->range.lower;
|
|
dstval->range.upper = srcval->range.upper;
|
|
}
|
|
break;
|
|
|
|
case IPP_TAG_TEXTLANG :
|
|
case IPP_TAG_NAMELANG :
|
|
dstattr = ippAddStrings(dst, srcattr->group_tag,
|
|
(ipp_tag_t)(srcattr->value_tag | quickcopy),
|
|
srcattr->name, srcattr->num_values, NULL, NULL);
|
|
if (!dstattr)
|
|
break;
|
|
|
|
if (quickcopy)
|
|
{
|
|
for (i = srcattr->num_values, srcval = srcattr->values,
|
|
dstval = dstattr->values;
|
|
i > 0;
|
|
i --, srcval ++, dstval ++)
|
|
{
|
|
dstval->string.language = srcval->string.language;
|
|
dstval->string.text = srcval->string.text;
|
|
}
|
|
}
|
|
else if (srcattr->value_tag & IPP_TAG_COPY)
|
|
{
|
|
for (i = srcattr->num_values, srcval = srcattr->values,
|
|
dstval = dstattr->values;
|
|
i > 0;
|
|
i --, srcval ++, dstval ++)
|
|
{
|
|
if (srcval == srcattr->values)
|
|
dstval->string.language = _cupsStrAlloc(srcval->string.language);
|
|
else
|
|
dstval->string.language = dstattr->values[0].string.language;
|
|
|
|
dstval->string.text = _cupsStrAlloc(srcval->string.text);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = srcattr->num_values, srcval = srcattr->values,
|
|
dstval = dstattr->values;
|
|
i > 0;
|
|
i --, srcval ++, dstval ++)
|
|
{
|
|
if (srcval == srcattr->values)
|
|
dstval->string.language = _cupsStrRetain(srcval->string.language);
|
|
else
|
|
dstval->string.language = dstattr->values[0].string.language;
|
|
|
|
dstval->string.text = _cupsStrRetain(srcval->string.text);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IPP_TAG_BEGIN_COLLECTION :
|
|
dstattr = ippAddCollections(dst, srcattr->group_tag, srcattr->name,
|
|
srcattr->num_values, NULL);
|
|
if (!dstattr)
|
|
break;
|
|
|
|
for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
|
|
i > 0;
|
|
i --, srcval ++, dstval ++)
|
|
{
|
|
dstval->collection = srcval->collection;
|
|
srcval->collection->use ++;
|
|
}
|
|
break;
|
|
|
|
case IPP_TAG_STRING :
|
|
default :
|
|
/* TODO: Implement quick copy for unknown/octetString values */
|
|
dstattr = ippAddIntegers(dst, srcattr->group_tag, srcattr->value_tag,
|
|
srcattr->name, srcattr->num_values, NULL);
|
|
if (!dstattr)
|
|
break;
|
|
|
|
for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
|
|
i > 0;
|
|
i --, srcval ++, dstval ++)
|
|
{
|
|
dstval->unknown.length = srcval->unknown.length;
|
|
|
|
if (dstval->unknown.length > 0)
|
|
{
|
|
if ((dstval->unknown.data = malloc(dstval->unknown.length)) == NULL)
|
|
dstval->unknown.length = 0;
|
|
else
|
|
memcpy(dstval->unknown.data, srcval->unknown.data, dstval->unknown.length);
|
|
}
|
|
}
|
|
break; /* anti-compiler-warning-code */
|
|
}
|
|
|
|
return (dstattr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippCopyAttributes()' - Copy attributes from one IPP message to another.
|
|
*
|
|
* Zero or more attributes are copied from the source IPP message, @code@ src, to the
|
|
* destination IPP message, @code dst@. When @code quickcopy@ is non-zero, a "shallow"
|
|
* reference copy of the attribute is created - this should only be done as long as the
|
|
* original source IPP message will not be freed for the life of the destination.
|
|
*
|
|
* The @code cb@ and @code context@ parameters provide a generic way to "filter" the
|
|
* attributes that are copied - the function must return 1 to copy the attribute or
|
|
* 0 to skip it. The function may also choose to do a partial copy of the source attribute
|
|
* itself.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on error */
|
|
ippCopyAttributes(
|
|
ipp_t *dst, /* I - Destination IPP message */
|
|
ipp_t *src, /* I - Source IPP message */
|
|
int quickcopy, /* I - 1 for a referenced copy, 0 for normal */
|
|
ipp_copycb_t cb, /* I - Copy callback or @code NULL@ for none */
|
|
void *context) /* I - Context pointer */
|
|
{
|
|
ipp_attribute_t *srcattr; /* Source attribute */
|
|
|
|
|
|
DEBUG_printf(("ippCopyAttributes(dst=%p, src=%p, quickcopy=%d, cb=%p, context=%p)",
|
|
dst, src, quickcopy, cb, context));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!dst || !src)
|
|
return (0);
|
|
|
|
/*
|
|
* Loop through source attributes and copy as needed...
|
|
*/
|
|
|
|
for (srcattr = src->attrs; srcattr; srcattr = srcattr->next)
|
|
if (!cb || (*cb)(context, dst, srcattr))
|
|
if (!ippCopyAttribute(dst, srcattr, quickcopy))
|
|
return (0);
|
|
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippDateToTime()' - Convert from RFC 1903 Date/Time format to UNIX time
|
|
* in seconds.
|
|
*/
|
|
|
|
time_t /* O - UNIX time value */
|
|
ippDateToTime(const ipp_uchar_t *date) /* I - RFC 1903 date info */
|
|
{
|
|
struct tm unixdate; /* UNIX date/time info */
|
|
time_t t; /* Computed time */
|
|
|
|
|
|
if (!date)
|
|
return (0);
|
|
|
|
memset(&unixdate, 0, sizeof(unixdate));
|
|
|
|
/*
|
|
* RFC-1903 date/time format is:
|
|
*
|
|
* Byte(s) Description
|
|
* ------- -----------
|
|
* 0-1 Year (0 to 65535)
|
|
* 2 Month (1 to 12)
|
|
* 3 Day (1 to 31)
|
|
* 4 Hours (0 to 23)
|
|
* 5 Minutes (0 to 59)
|
|
* 6 Seconds (0 to 60, 60 = "leap second")
|
|
* 7 Deciseconds (0 to 9)
|
|
* 8 +/- UTC
|
|
* 9 UTC hours (0 to 11)
|
|
* 10 UTC minutes (0 to 59)
|
|
*/
|
|
|
|
unixdate.tm_year = ((date[0] << 8) | date[1]) - 1900;
|
|
unixdate.tm_mon = date[2] - 1;
|
|
unixdate.tm_mday = date[3];
|
|
unixdate.tm_hour = date[4];
|
|
unixdate.tm_min = date[5];
|
|
unixdate.tm_sec = date[6];
|
|
|
|
t = mktime(&unixdate);
|
|
|
|
if (date[8] == '-')
|
|
t += date[9] * 3600 + date[10] * 60;
|
|
else
|
|
t -= date[9] * 3600 + date[10] * 60;
|
|
|
|
return (t);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippDelete()' - Delete an IPP message.
|
|
*/
|
|
|
|
void
|
|
ippDelete(ipp_t *ipp) /* I - IPP message */
|
|
{
|
|
ipp_attribute_t *attr, /* Current attribute */
|
|
*next; /* Next attribute */
|
|
|
|
|
|
DEBUG_printf(("ippDelete(ipp=%p)", ipp));
|
|
|
|
if (!ipp)
|
|
return;
|
|
|
|
ipp->use --;
|
|
if (ipp->use > 0)
|
|
return;
|
|
|
|
for (attr = ipp->attrs; attr != NULL; attr = next)
|
|
{
|
|
next = attr->next;
|
|
|
|
ipp_free_values(attr, 0, attr->num_values);
|
|
|
|
if (attr->name)
|
|
_cupsStrFree(attr->name);
|
|
|
|
free(attr);
|
|
}
|
|
|
|
free(ipp);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippDeleteAttribute()' - Delete a single attribute in an IPP message.
|
|
*
|
|
* @since CUPS 1.1.19/OS X 10.3@
|
|
*/
|
|
|
|
void
|
|
ippDeleteAttribute(
|
|
ipp_t *ipp, /* I - IPP message */
|
|
ipp_attribute_t *attr) /* I - Attribute to delete */
|
|
{
|
|
ipp_attribute_t *current, /* Current attribute */
|
|
*prev; /* Previous attribute */
|
|
|
|
|
|
DEBUG_printf(("ippDeleteAttribute(ipp=%p, attr=%p(%s))", ipp, attr,
|
|
attr ? attr->name : "(null)"));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!attr)
|
|
return;
|
|
|
|
/*
|
|
* Find the attribute in the list...
|
|
*/
|
|
|
|
if (ipp)
|
|
{
|
|
for (current = ipp->attrs, prev = NULL;
|
|
current;
|
|
prev = current, current = current->next)
|
|
if (current == attr)
|
|
{
|
|
/*
|
|
* Found it, remove the attribute from the list...
|
|
*/
|
|
|
|
if (prev)
|
|
prev->next = current->next;
|
|
else
|
|
ipp->attrs = current->next;
|
|
|
|
if (current == ipp->last)
|
|
ipp->last = prev;
|
|
|
|
break;
|
|
}
|
|
|
|
if (!current)
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Free memory used by the attribute...
|
|
*/
|
|
|
|
ipp_free_values(attr, 0, attr->num_values);
|
|
|
|
if (attr->name)
|
|
_cupsStrFree(attr->name);
|
|
|
|
free(attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippDeleteValues()' - Delete values in an attribute.
|
|
*
|
|
* The @code element@ parameter specifies the first value to delete, starting at
|
|
* 0. It must be less than the number of values returned by @link ippGetCount@.
|
|
*
|
|
* The @code attr@ parameter may be modified as a result of setting the value.
|
|
*
|
|
* Deleting all values in an attribute deletes the attribute.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippDeleteValues(
|
|
ipp_t *ipp, /* I - IPP message */
|
|
ipp_attribute_t **attr, /* IO - Attribute */
|
|
int element, /* I - Index of first value to delete (0-based) */
|
|
int count) /* I - Number of values to delete */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !attr || !*attr ||
|
|
element < 0 || element >= (*attr)->num_values || count <= 0 ||
|
|
(element + count) >= (*attr)->num_values)
|
|
return (0);
|
|
|
|
/*
|
|
* If we are deleting all values, just delete the attribute entirely.
|
|
*/
|
|
|
|
if (count == (*attr)->num_values)
|
|
{
|
|
ippDeleteAttribute(ipp, *attr);
|
|
*attr = NULL;
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Otherwise free the values in question and return.
|
|
*/
|
|
|
|
ipp_free_values(*attr, element, count);
|
|
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippFindAttribute()' - Find a named attribute in a request.
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - Matching attribute */
|
|
ippFindAttribute(ipp_t *ipp, /* I - IPP message */
|
|
const char *name, /* I - Name of attribute */
|
|
ipp_tag_t type) /* I - Type of attribute */
|
|
{
|
|
DEBUG_printf(("2ippFindAttribute(ipp=%p, name=\"%s\", type=%02x(%s))", ipp,
|
|
name, type, ippTagString(type)));
|
|
|
|
if (!ipp || !name)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Reset the current pointer...
|
|
*/
|
|
|
|
ipp->current = NULL;
|
|
|
|
/*
|
|
* Search for the attribute...
|
|
*/
|
|
|
|
return (ippFindNextAttribute(ipp, name, type));
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippFindNextAttribute()' - Find the next named attribute in a request.
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - Matching attribute */
|
|
ippFindNextAttribute(ipp_t *ipp, /* I - IPP message */
|
|
const char *name, /* I - Name of attribute */
|
|
ipp_tag_t type) /* I - Type of attribute */
|
|
{
|
|
ipp_attribute_t *attr; /* Current atttribute */
|
|
ipp_tag_t value_tag; /* Value tag */
|
|
|
|
|
|
DEBUG_printf(("2ippFindNextAttribute(ipp=%p, name=\"%s\", type=%02x(%s))",
|
|
ipp, name, type, ippTagString(type)));
|
|
|
|
if (!ipp || !name)
|
|
return (NULL);
|
|
|
|
if (ipp->current)
|
|
{
|
|
ipp->prev = ipp->current;
|
|
attr = ipp->current->next;
|
|
}
|
|
else
|
|
{
|
|
ipp->prev = NULL;
|
|
attr = ipp->attrs;
|
|
}
|
|
|
|
for (; attr != NULL; ipp->prev = attr, attr = attr->next)
|
|
{
|
|
DEBUG_printf(("4ippFindAttribute: attr=%p, name=\"%s\"", attr,
|
|
attr->name));
|
|
|
|
value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_MASK);
|
|
|
|
if (attr->name != NULL && _cups_strcasecmp(attr->name, name) == 0 &&
|
|
(value_tag == type || type == IPP_TAG_ZERO ||
|
|
(value_tag == IPP_TAG_TEXTLANG && type == IPP_TAG_TEXT) ||
|
|
(value_tag == IPP_TAG_NAMELANG && type == IPP_TAG_NAME)))
|
|
{
|
|
ipp->current = attr;
|
|
|
|
return (attr);
|
|
}
|
|
}
|
|
|
|
ipp->current = NULL;
|
|
ipp->prev = NULL;
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippFirstAttribute()' - Return the first attribute in the message.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - First attribute or @code NULL@ if none */
|
|
ippFirstAttribute(ipp_t *ipp) /* I - IPP message */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Return the first attribute...
|
|
*/
|
|
|
|
return (ipp->current = ipp->attrs);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetBoolean()' - Get a boolean value for an attribute.
|
|
*
|
|
* The @code element@ parameter specifies which value to get from 0 to
|
|
* @link ippGetCount(attr)@ - 1.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - Boolean value or -1 on error */
|
|
ippGetBoolean(ipp_attribute_t *attr, /* I - IPP attribute */
|
|
int element) /* I - Value number (0-based) */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!attr || attr->value_tag != IPP_TAG_BOOLEAN ||
|
|
element < 0 || element >= attr->num_values)
|
|
return (-1);
|
|
|
|
/*
|
|
* Return the value...
|
|
*/
|
|
|
|
return (attr->values[element].boolean);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetCollection()' - Get a collection value for an attribute.
|
|
*
|
|
* The @code element@ parameter specifies which value to get from 0 to
|
|
* @link ippGetCount(attr)@ - 1.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
ipp_t * /* O - Collection value or @code NULL@ on error */
|
|
ippGetCollection(
|
|
ipp_attribute_t *attr, /* I - IPP attribute */
|
|
int element) /* I - Value number (0-based) */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!attr || attr->value_tag != IPP_TAG_BEGIN_COLLECTION ||
|
|
element < 0 || element >= attr->num_values)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Return the value...
|
|
*/
|
|
|
|
return (attr->values[element].collection);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetCount()' - Get the number of values in an attribute.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - Number of values or -1 on error */
|
|
ippGetCount(ipp_attribute_t *attr) /* I - IPP attribute */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!attr)
|
|
return (-1);
|
|
|
|
/*
|
|
* Return the number of values...
|
|
*/
|
|
|
|
return (attr->num_values);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetDate()' - Get a date value for an attribute.
|
|
*
|
|
* The @code element@ parameter specifies which value to get from 0 to
|
|
* @link ippGetCount(attr)@ - 1.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
const ipp_uchar_t * /* O - Date value or @code NULL@ */
|
|
ippGetDate(ipp_attribute_t *attr, /* I - IPP attribute */
|
|
int element) /* I - Value number (0-based) */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!attr || attr->value_tag != IPP_TAG_DATE ||
|
|
element < 0 || element >= attr->num_values)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Return the value...
|
|
*/
|
|
|
|
return (attr->values[element].date);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetGroupTag()' - Get the group associated with an attribute.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
ipp_tag_t /* O - Group tag or @code IPP_TAG_ZERO@ on error */
|
|
ippGetGroupTag(ipp_attribute_t *attr) /* I - IPP attribute */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!attr)
|
|
return (IPP_TAG_ZERO);
|
|
|
|
/*
|
|
* Return the group...
|
|
*/
|
|
|
|
return (attr->group_tag);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetInteger()' - Get the integer/enum value for an attribute.
|
|
*
|
|
* The @code element@ parameter specifies which value to get from 0 to
|
|
* @link ippGetCount(attr)@ - 1.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - Value or -1 on error */
|
|
ippGetInteger(ipp_attribute_t *attr, /* I - IPP attribute */
|
|
int element) /* I - Value number (0-based) */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!attr || (attr->value_tag != IPP_TAG_INTEGER && attr->value_tag != IPP_TAG_ENUM) ||
|
|
element < 0 || element >= attr->num_values)
|
|
return (-1);
|
|
|
|
/*
|
|
* Return the value...
|
|
*/
|
|
|
|
return (attr->values[element].integer);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetName()' - Get the attribute name.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
const char * /* O - Attribute name or @code NULL@ for separators */
|
|
ippGetName(ipp_attribute_t *attr) /* I - IPP attribute */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!attr)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Return the name...
|
|
*/
|
|
|
|
return (attr->name);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetOperation()' - Get the operation ID in an IPP message.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
ipp_op_t /* O - Operation ID or -1 on error */
|
|
ippGetOperation(ipp_t *ipp) /* I - IPP request message */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp)
|
|
return ((ipp_op_t)-1);
|
|
|
|
/*
|
|
* Return the value...
|
|
*/
|
|
|
|
return (ipp->request.op.operation_id);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetRange()' - Get a rangeOfInteger value from an attribute.
|
|
*
|
|
* The @code element@ parameter specifies which value to get from 0 to
|
|
* @link ippGetCount(attr)@ - 1.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - Lower value of range or -1 */
|
|
ippGetRange(ipp_attribute_t *attr, /* I - IPP attribute */
|
|
int element, /* I - Value number (0-based) */
|
|
int *uppervalue)/* O - Upper value of range */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!attr || attr->value_tag != IPP_TAG_RANGE ||
|
|
element < 0 || element >= attr->num_values)
|
|
{
|
|
if (uppervalue)
|
|
*uppervalue = -1;
|
|
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Return the values...
|
|
*/
|
|
|
|
if (uppervalue)
|
|
*uppervalue = attr->values[element].range.upper;
|
|
|
|
return (attr->values[element].range.lower);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetRequestId()' - Get the request ID from an IPP message.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - Request ID or -1 on error */
|
|
ippGetRequestId(ipp_t *ipp) /* I - IPP message */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp)
|
|
return (-1);
|
|
|
|
/*
|
|
* Return the request ID...
|
|
*/
|
|
|
|
return (ipp->request.any.request_id);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetResolution()' - Get a resolution value for an attribute.
|
|
*
|
|
* The @code element@ parameter specifies which value to get from 0 to
|
|
* @link ippGetCount(attr)@ - 1.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - Horizontal/cross feed resolution or -1 */
|
|
ippGetResolution(
|
|
ipp_attribute_t *attr, /* I - IPP attribute */
|
|
int element, /* I - Value number (0-based) */
|
|
int *yres, /* O - Vertical/feed resolution */
|
|
ipp_res_t *units) /* O - Units for resolution */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!attr || attr->value_tag != IPP_TAG_RESOLUTION ||
|
|
element < 0 || element >= attr->num_values)
|
|
return (-1);
|
|
|
|
/*
|
|
* Return the value...
|
|
*/
|
|
|
|
if (yres)
|
|
*yres = attr->values[element].resolution.yres;
|
|
|
|
if (units)
|
|
*units = attr->values[element].resolution.units;
|
|
|
|
return (attr->values[element].resolution.xres);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetState()' - Get the IPP message state.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
ipp_state_t /* O - IPP message state value */
|
|
ippGetState(ipp_t *ipp) /* I - IPP message */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp)
|
|
return (IPP_IDLE);
|
|
|
|
/*
|
|
* Return the value...
|
|
*/
|
|
|
|
return (ipp->state);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetStatusCode()' - Get the status code from an IPP response or event message.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
ipp_status_t /* O - Status code in IPP message */
|
|
ippGetStatusCode(ipp_t *ipp) /* I - IPP response or event message */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp)
|
|
return (IPP_INTERNAL_ERROR);
|
|
|
|
/*
|
|
* Return the value...
|
|
*/
|
|
|
|
return (ipp->request.status.status_code);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetString()' - Get the string and optionally the language code for an attribute.
|
|
*
|
|
* The @code element@ parameter specifies which value to get from 0 to
|
|
* @link ippGetCount(attr)@ - 1.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
const char *
|
|
ippGetString(ipp_attribute_t *attr, /* I - IPP attribute */
|
|
int element, /* I - Value number (0-based) */
|
|
const char **language)/* O - Language code (@code NULL@ for don't care) */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!attr || element < 0 || element >= attr->num_values ||
|
|
(attr->value_tag != IPP_TAG_TEXTLANG && attr->value_tag != IPP_TAG_NAMELANG &&
|
|
(attr->value_tag < IPP_TAG_TEXT || attr->value_tag > IPP_TAG_MIMETYPE)))
|
|
return (NULL);
|
|
|
|
/*
|
|
* Return the value...
|
|
*/
|
|
|
|
if (language)
|
|
*language = attr->values[element].string.language;
|
|
|
|
return (attr->values[element].string.text);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetValueTag()' - Get the value tag for an attribute.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
ipp_tag_t /* O - Value tag or @code IPP_TAG_ZERO@ on error */
|
|
ippGetValueTag(ipp_attribute_t *attr) /* I - IPP attribute */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!attr)
|
|
return (IPP_TAG_ZERO);
|
|
|
|
/*
|
|
* Return the value...
|
|
*/
|
|
|
|
return (attr->value_tag);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippGetVersion()' - Get the major and minor version number from an IPP message.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - Major version number or -1 on error */
|
|
ippGetVersion(ipp_t *ipp, /* I - IPP message */
|
|
int *minor) /* O - Minor version number or @code NULL@ */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp)
|
|
{
|
|
if (minor)
|
|
*minor = -1;
|
|
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Return the value...
|
|
*/
|
|
|
|
if (minor)
|
|
*minor = ipp->request.any.version[1];
|
|
|
|
return (ipp->request.any.version[0]);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippLength()' - Compute the length of an IPP message.
|
|
*/
|
|
|
|
size_t /* O - Size of IPP message */
|
|
ippLength(ipp_t *ipp) /* I - IPP message */
|
|
{
|
|
return (ipp_length(ipp, 0));
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippNextAttribute()' - Return the next attribute in the message.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
ipp_attribute_t * /* O - Next attribute or @code NULL@ if none */
|
|
ippNextAttribute(ipp_t *ipp) /* I - IPP message */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !ipp->current)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Return the next attribute...
|
|
*/
|
|
|
|
return (ipp->current = ipp->current->next);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippNew()' - Allocate a new IPP message.
|
|
*/
|
|
|
|
ipp_t * /* O - New IPP message */
|
|
ippNew(void)
|
|
{
|
|
ipp_t *temp; /* New IPP message */
|
|
_cups_globals_t *cg = _cupsGlobals();
|
|
/* Global data */
|
|
|
|
|
|
DEBUG_puts("ippNew()");
|
|
|
|
if ((temp = (ipp_t *)calloc(1, sizeof(ipp_t))) != NULL)
|
|
{
|
|
/*
|
|
* Set default version - usually 2.0...
|
|
*/
|
|
|
|
if (cg->server_version == 0)
|
|
_cupsSetDefaults();
|
|
|
|
temp->request.any.version[0] = cg->server_version / 10;
|
|
temp->request.any.version[1] = cg->server_version % 10;
|
|
temp->use = 1;
|
|
}
|
|
|
|
DEBUG_printf(("1ippNew: Returning %p", temp));
|
|
|
|
return (temp);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippNewRequest()' - Allocate a new IPP request message.
|
|
*
|
|
* The new request message is initialized with the attributes-charset and
|
|
* attributes-natural-language attributes added. The
|
|
* attributes-natural-language value is derived from the current locale.
|
|
*
|
|
* @since CUPS 1.2/OS X 10.5@
|
|
*/
|
|
|
|
ipp_t * /* O - IPP request message */
|
|
ippNewRequest(ipp_op_t op) /* I - Operation code */
|
|
{
|
|
ipp_t *request; /* IPP request message */
|
|
cups_lang_t *language; /* Current language localization */
|
|
static int request_id = 0; /* Current request ID */
|
|
static _cups_mutex_t request_mutex = _CUPS_MUTEX_INITIALIZER;
|
|
/* Mutex for request ID */
|
|
|
|
|
|
DEBUG_printf(("ippNewRequest(op=%02x(%s))", op, ippOpString(op)));
|
|
|
|
/*
|
|
* Create a new IPP message...
|
|
*/
|
|
|
|
if ((request = ippNew()) == NULL)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Set the operation and request ID...
|
|
*/
|
|
|
|
_cupsMutexLock(&request_mutex);
|
|
|
|
request->request.op.operation_id = op;
|
|
request->request.op.request_id = ++request_id;
|
|
|
|
_cupsMutexUnlock(&request_mutex);
|
|
|
|
/*
|
|
* Use UTF-8 as the character set...
|
|
*/
|
|
|
|
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
|
|
"attributes-charset", NULL, "utf-8");
|
|
|
|
/*
|
|
* Get the language from the current locale...
|
|
*/
|
|
|
|
language = cupsLangDefault();
|
|
|
|
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
|
|
"attributes-natural-language", NULL, language->language);
|
|
|
|
/*
|
|
* Return the new request...
|
|
*/
|
|
|
|
return (request);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippRead()' - Read data for an IPP message from a HTTP connection.
|
|
*/
|
|
|
|
ipp_state_t /* O - Current state */
|
|
ippRead(http_t *http, /* I - HTTP connection */
|
|
ipp_t *ipp) /* I - IPP data */
|
|
{
|
|
DEBUG_printf(("ippRead(http=%p, ipp=%p), data_remaining=" CUPS_LLFMT,
|
|
http, ipp, CUPS_LLCAST (http ? http->data_remaining : -1)));
|
|
|
|
if (!http)
|
|
return (IPP_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));
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippReadFile()' - Read data for an IPP message from a file.
|
|
*
|
|
* @since CUPS 1.1.19/OS X 10.3@
|
|
*/
|
|
|
|
ipp_state_t /* O - Current state */
|
|
ippReadFile(int fd, /* I - HTTP data */
|
|
ipp_t *ipp) /* I - IPP data */
|
|
{
|
|
DEBUG_printf(("ippReadFile(fd=%d, ipp=%p)", fd, ipp));
|
|
|
|
return (ippReadIO(&fd, (ipp_iocb_t)ipp_read_file, 1, NULL, ipp));
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippReadIO()' - Read data for an IPP message.
|
|
*
|
|
* @since CUPS 1.2/OS X 10.5@
|
|
*/
|
|
|
|
ipp_state_t /* O - Current state */
|
|
ippReadIO(void *src, /* I - Data source */
|
|
ipp_iocb_t cb, /* I - Read callback function */
|
|
int blocking, /* I - Use blocking IO? */
|
|
ipp_t *parent, /* I - Parent request, if any */
|
|
ipp_t *ipp) /* I - IPP data */
|
|
{
|
|
int n; /* Length of data */
|
|
unsigned char *buffer, /* Data buffer */
|
|
string[IPP_MAX_NAME],
|
|
/* Small string buffer */
|
|
*bufptr; /* Pointer into buffer */
|
|
ipp_attribute_t *attr; /* 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)",
|
|
src, cb, blocking, parent, ipp));
|
|
DEBUG_printf(("2ippReadIO: ipp->state=%d", ipp ? ipp->state : IPP_ERROR));
|
|
|
|
if (!src || !ipp)
|
|
return (IPP_ERROR);
|
|
|
|
if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL)
|
|
{
|
|
DEBUG_puts("1ippReadIO: Unable to get read buffer.");
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
switch (ipp->state)
|
|
{
|
|
case IPP_IDLE :
|
|
ipp->state ++; /* Avoid common problem... */
|
|
|
|
case IPP_HEADER :
|
|
if (parent == NULL)
|
|
{
|
|
/*
|
|
* Get the request header...
|
|
*/
|
|
|
|
if ((*cb)(src, buffer, 8) < 8)
|
|
{
|
|
DEBUG_puts("1ippReadIO: Unable to read header.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
/*
|
|
* Then copy the request header over...
|
|
*/
|
|
|
|
ipp->request.any.version[0] = buffer[0];
|
|
ipp->request.any.version[1] = buffer[1];
|
|
ipp->request.any.op_status = (buffer[2] << 8) | buffer[3];
|
|
ipp->request.any.request_id = (((((buffer[4] << 8) | buffer[5]) << 8) |
|
|
buffer[6]) << 8) | buffer[7];
|
|
|
|
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_ATTRIBUTE;
|
|
ipp->current = NULL;
|
|
ipp->curtag = IPP_TAG_ZERO;
|
|
ipp->prev = ipp->last;
|
|
|
|
/*
|
|
* If blocking is disabled, stop here...
|
|
*/
|
|
|
|
if (!blocking)
|
|
break;
|
|
|
|
case IPP_ATTRIBUTE :
|
|
for (;;)
|
|
{
|
|
if ((*cb)(src, buffer, 1) < 1)
|
|
{
|
|
DEBUG_puts("1ippReadIO: Callback returned EOF/error");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
DEBUG_printf(("2ippReadIO: ipp->current=%p, ipp->prev=%p",
|
|
ipp->current, ipp->prev));
|
|
|
|
/*
|
|
* Read this attribute...
|
|
*/
|
|
|
|
tag = (ipp_tag_t)buffer[0];
|
|
if (tag == IPP_TAG_EXTENSION)
|
|
{
|
|
/*
|
|
* Read 32-bit "extension" tag...
|
|
*/
|
|
|
|
if ((*cb)(src, buffer, 4) < 1)
|
|
{
|
|
DEBUG_puts("1ippReadIO: Callback returned EOF/error");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
tag = (ipp_tag_t)((((((buffer[0] << 8) | buffer[1]) << 8) |
|
|
buffer[2]) << 8) | buffer[3]);
|
|
|
|
if (tag & IPP_TAG_COPY)
|
|
{
|
|
/*
|
|
* Fail if the high bit is set in the tag...
|
|
*/
|
|
|
|
_cupsSetError(IPP_INTERNAL_ERROR, _("IPP extension tag larger than 0x7FFFFFFF."), 1);
|
|
DEBUG_printf(("1ippReadIO: bad tag 0x%x.", tag));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
}
|
|
|
|
if (tag == IPP_TAG_END)
|
|
{
|
|
/*
|
|
* No more attributes left...
|
|
*/
|
|
|
|
DEBUG_puts("2ippReadIO: IPP_TAG_END.");
|
|
|
|
ipp->state = IPP_DATA;
|
|
break;
|
|
}
|
|
else if (tag < IPP_TAG_UNSUPPORTED_VALUE)
|
|
{
|
|
/*
|
|
* Group tag... Set the current group and continue...
|
|
*/
|
|
|
|
if (ipp->curtag == tag)
|
|
ipp->prev = ippAddSeparator(ipp);
|
|
else if (ipp->current)
|
|
ipp->prev = ipp->current;
|
|
|
|
ipp->curtag = tag;
|
|
ipp->current = NULL;
|
|
DEBUG_printf(("2ippReadIO: group tag=%x(%s), ipp->prev=%p", tag,
|
|
ippTagString(tag), ipp->prev));
|
|
continue;
|
|
}
|
|
|
|
DEBUG_printf(("2ippReadIO: value tag=%x(%s)", tag,
|
|
ippTagString(tag)));
|
|
|
|
/*
|
|
* Get the name...
|
|
*/
|
|
|
|
if ((*cb)(src, buffer, 2) < 2)
|
|
{
|
|
DEBUG_puts("1ippReadIO: unable to read name length.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
n = (buffer[0] << 8) | buffer[1];
|
|
|
|
if (n >= IPP_BUF_SIZE)
|
|
{
|
|
_cupsSetError(IPP_INTERNAL_ERROR, _("IPP name larger than 32767 bytes."), 1);
|
|
DEBUG_printf(("1ippReadIO: bad name length %d.", n));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
DEBUG_printf(("2ippReadIO: name length=%d", n));
|
|
|
|
if (n == 0 && tag != IPP_TAG_MEMBERNAME &&
|
|
tag != IPP_TAG_END_COLLECTION)
|
|
{
|
|
/*
|
|
* More values for current attribute...
|
|
*/
|
|
|
|
if (ipp->current == NULL)
|
|
{
|
|
_cupsSetError(IPP_INTERNAL_ERROR, _("IPP attribute has no name."), 1);
|
|
DEBUG_puts("1ippReadIO: Attribute without name and no current.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
attr = ipp->current;
|
|
value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_MASK);
|
|
|
|
/*
|
|
* Make sure we aren't adding a new value of a different
|
|
* type...
|
|
*/
|
|
|
|
if (value_tag == IPP_TAG_ZERO)
|
|
{
|
|
/*
|
|
* Setting the value of a collection member...
|
|
*/
|
|
|
|
attr->value_tag = tag;
|
|
}
|
|
else if (value_tag == IPP_TAG_TEXTLANG ||
|
|
value_tag == IPP_TAG_NAMELANG ||
|
|
(value_tag >= IPP_TAG_TEXT &&
|
|
value_tag <= IPP_TAG_MIMETYPE))
|
|
{
|
|
/*
|
|
* String values can sometimes come across in different
|
|
* forms; accept sets of differing values...
|
|
*/
|
|
|
|
if (tag != IPP_TAG_TEXTLANG && tag != IPP_TAG_NAMELANG &&
|
|
(tag < IPP_TAG_TEXT || tag > IPP_TAG_MIMETYPE) &&
|
|
tag != IPP_TAG_NOVALUE)
|
|
{
|
|
_cupsSetError(IPP_INTERNAL_ERROR,
|
|
_("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)));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
if (value_tag != tag)
|
|
{
|
|
DEBUG_printf(("1ippReadIO: Converting %s attribute from %s to %s.",
|
|
attr->name, ippTagString(value_tag), ippTagString(tag)));
|
|
ippSetValueTag(ipp, &attr, tag);
|
|
}
|
|
}
|
|
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_INTERNAL_ERROR,
|
|
_("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)));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
if (value_tag == IPP_TAG_INTEGER && tag == IPP_TAG_RANGE)
|
|
{
|
|
/*
|
|
* Convert integer values to rangeOfInteger values...
|
|
*/
|
|
|
|
DEBUG_printf(("1ippReadIO: Converting %s attribute to "
|
|
"rangeOfInteger.", attr->name));
|
|
ippSetValueTag(ipp, &attr, IPP_TAG_RANGE);
|
|
}
|
|
}
|
|
else if (value_tag != tag)
|
|
{
|
|
_cupsSetError(IPP_INTERNAL_ERROR,
|
|
_("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)));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
/*
|
|
* Finally, reallocate the attribute array as needed...
|
|
*/
|
|
|
|
if ((value = ipp_set_value(ipp, &attr, attr->num_values)) == NULL)
|
|
{
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
}
|
|
else if (tag == IPP_TAG_MEMBERNAME)
|
|
{
|
|
/*
|
|
* Name must be length 0!
|
|
*/
|
|
|
|
if (n)
|
|
{
|
|
_cupsSetError(IPP_INTERNAL_ERROR, _("IPP member name is not empty."), 1);
|
|
DEBUG_puts("1ippReadIO: member name not empty.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
if (ipp->current)
|
|
ipp->prev = ipp->current;
|
|
|
|
attr = ipp->current = ipp_add_attr(ipp, NULL, ipp->curtag, IPP_TAG_ZERO, 1);
|
|
if (!attr)
|
|
{
|
|
_cupsSetHTTPError(HTTP_ERROR);
|
|
DEBUG_puts("1ippReadIO: unable to allocate attribute.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
DEBUG_printf(("2ippReadIO: membername, ipp->current=%p, ipp->prev=%p",
|
|
ipp->current, ipp->prev));
|
|
|
|
value = attr->values;
|
|
}
|
|
else if (tag != IPP_TAG_END_COLLECTION)
|
|
{
|
|
/*
|
|
* New attribute; read the name and add it...
|
|
*/
|
|
|
|
if ((*cb)(src, buffer, n) < n)
|
|
{
|
|
DEBUG_puts("1ippReadIO: unable to read name.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
buffer[n] = '\0';
|
|
|
|
if (ipp->current)
|
|
ipp->prev = ipp->current;
|
|
|
|
if ((attr = ipp->current = ipp_add_attr(ipp, (char *)buffer, ipp->curtag, tag,
|
|
1)) == NULL)
|
|
{
|
|
_cupsSetHTTPError(HTTP_ERROR);
|
|
DEBUG_puts("1ippReadIO: unable to allocate attribute.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
DEBUG_printf(("2ippReadIO: name=\"%s\", ipp->current=%p, "
|
|
"ipp->prev=%p", buffer, ipp->current, ipp->prev));
|
|
|
|
value = attr->values;
|
|
}
|
|
else
|
|
{
|
|
attr = NULL;
|
|
value = NULL;
|
|
}
|
|
|
|
if ((*cb)(src, buffer, 2) < 2)
|
|
{
|
|
DEBUG_puts("1ippReadIO: unable to read value length.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
n = (buffer[0] << 8) | buffer[1];
|
|
DEBUG_printf(("2ippReadIO: value length=%d", n));
|
|
|
|
if (n >= IPP_BUF_SIZE)
|
|
{
|
|
_cupsSetError(IPP_INTERNAL_ERROR,
|
|
_("IPP value larger than 32767 bytes."), 1);
|
|
DEBUG_printf(("1ippReadIO: bad value length %d.", n));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
switch (tag)
|
|
{
|
|
case IPP_TAG_INTEGER :
|
|
case IPP_TAG_ENUM :
|
|
if (n != 4)
|
|
{
|
|
if (tag == IPP_TAG_INTEGER)
|
|
_cupsSetError(IPP_INTERNAL_ERROR,
|
|
_("IPP integer value not 4 bytes."), 1);
|
|
else
|
|
_cupsSetError(IPP_INTERNAL_ERROR,
|
|
_("IPP enum value not 4 bytes."), 1);
|
|
DEBUG_printf(("1ippReadIO: bad integer value length %d.", n));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
if ((*cb)(src, buffer, 4) < 4)
|
|
{
|
|
DEBUG_puts("1ippReadIO: Unable to read integer value.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
n = (((((buffer[0] << 8) | buffer[1]) << 8) | 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_INTERNAL_ERROR, _("IPP boolean value not 1 byte."),
|
|
1);
|
|
DEBUG_printf(("1ippReadIO: bad boolean value length %d.", n));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
if ((*cb)(src, buffer, 1) < 1)
|
|
{
|
|
DEBUG_puts("1ippReadIO: Unable to read boolean value.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
value->boolean = buffer[0];
|
|
break;
|
|
|
|
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;
|
|
|
|
attr->value_tag = IPP_TAG_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 :
|
|
if (n > 0)
|
|
{
|
|
if ((*cb)(src, buffer, n) < n)
|
|
{
|
|
DEBUG_puts("1ippReadIO: unable to read string value.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
}
|
|
|
|
buffer[n] = '\0';
|
|
value->string.text = _cupsStrAlloc((char *)buffer);
|
|
DEBUG_printf(("2ippReadIO: value=\"%s\"", value->string.text));
|
|
break;
|
|
|
|
case IPP_TAG_DATE :
|
|
if (n != 11)
|
|
{
|
|
_cupsSetError(IPP_INTERNAL_ERROR, _("IPP date value not 11 bytes."), 1);
|
|
DEBUG_printf(("1ippReadIO: bad date value length %d.", n));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
if ((*cb)(src, value->date, 11) < 11)
|
|
{
|
|
DEBUG_puts("1ippReadIO: Unable to read date value.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
break;
|
|
|
|
case IPP_TAG_RESOLUTION :
|
|
if (n != 9)
|
|
{
|
|
_cupsSetError(IPP_INTERNAL_ERROR,
|
|
_("IPP resolution value not 9 bytes."), 1);
|
|
DEBUG_printf(("1ippReadIO: bad resolution value length %d.", n));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
if ((*cb)(src, buffer, 9) < 9)
|
|
{
|
|
DEBUG_puts("1ippReadIO: Unable to read resolution value.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
value->resolution.xres =
|
|
(((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
|
|
buffer[3];
|
|
value->resolution.yres =
|
|
(((((buffer[4] << 8) | buffer[5]) << 8) | buffer[6]) << 8) |
|
|
buffer[7];
|
|
value->resolution.units =
|
|
(ipp_res_t)buffer[8];
|
|
break;
|
|
|
|
case IPP_TAG_RANGE :
|
|
if (n != 8)
|
|
{
|
|
_cupsSetError(IPP_INTERNAL_ERROR,
|
|
_("IPP rangeOfInteger value not 8 bytes."), 1);
|
|
DEBUG_printf(("1ippReadIO: bad rangeOfInteger value length "
|
|
"%d.", n));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
if ((*cb)(src, buffer, 8) < 8)
|
|
{
|
|
DEBUG_puts("1ippReadIO: Unable to read range value.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
value->range.lower =
|
|
(((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
|
|
buffer[3];
|
|
value->range.upper =
|
|
(((((buffer[4] << 8) | buffer[5]) << 8) | buffer[6]) << 8) |
|
|
buffer[7];
|
|
break;
|
|
|
|
case IPP_TAG_TEXTLANG :
|
|
case IPP_TAG_NAMELANG :
|
|
if (n < 4)
|
|
{
|
|
if (tag == IPP_TAG_TEXTLANG)
|
|
_cupsSetError(IPP_INTERNAL_ERROR,
|
|
_("IPP textWithLanguage value less than "
|
|
"minimum 4 bytes."), 1);
|
|
else
|
|
_cupsSetError(IPP_INTERNAL_ERROR,
|
|
_("IPP nameWithLanguage value less than "
|
|
"minimum 4 bytes."), 1);
|
|
DEBUG_printf(("1ippReadIO: bad stringWithLanguage value "
|
|
"length %d.", n));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
if ((*cb)(src, buffer, n) < n)
|
|
{
|
|
DEBUG_puts("1ippReadIO: Unable to read string w/language "
|
|
"value.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
bufptr = buffer;
|
|
|
|
/*
|
|
* text-with-language and name-with-language are composite
|
|
* values:
|
|
*
|
|
* language-length
|
|
* language
|
|
* text-length
|
|
* text
|
|
*/
|
|
|
|
n = (bufptr[0] << 8) | bufptr[1];
|
|
|
|
if ((bufptr + 2 + n) >= (buffer + IPP_BUF_SIZE) ||
|
|
n >= sizeof(string))
|
|
{
|
|
_cupsSetError(IPP_INTERNAL_ERROR,
|
|
_("IPP language length overflows value."), 1);
|
|
DEBUG_printf(("1ippReadIO: bad language value length %d.",
|
|
n));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
memcpy(string, bufptr + 2, n);
|
|
string[n] = '\0';
|
|
|
|
value->string.language = _cupsStrAlloc((char *)string);
|
|
|
|
bufptr += 2 + n;
|
|
n = (bufptr[0] << 8) | bufptr[1];
|
|
|
|
if ((bufptr + 2 + n) >= (buffer + IPP_BUF_SIZE))
|
|
{
|
|
_cupsSetError(IPP_INTERNAL_ERROR,
|
|
_("IPP string length overflows value."), 1);
|
|
DEBUG_printf(("1ippReadIO: bad string value length %d.", n));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
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_INTERNAL_ERROR,
|
|
_("IPP begCollection value not 0 bytes."), 1);
|
|
DEBUG_puts("1ippReadIO: begCollection tag with value length "
|
|
"> 0.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
if (ippReadIO(src, cb, 1, ipp, value->collection) == IPP_ERROR)
|
|
{
|
|
DEBUG_puts("1ippReadIO: Unable to read collection value.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
break;
|
|
|
|
case IPP_TAG_END_COLLECTION :
|
|
_cupsBufferRelease((char *)buffer);
|
|
|
|
if (n > 0)
|
|
{
|
|
_cupsSetError(IPP_INTERNAL_ERROR,
|
|
_("IPP endCollection value not 0 bytes."), 1);
|
|
DEBUG_puts("1ippReadIO: endCollection tag with value length "
|
|
"> 0.");
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
DEBUG_puts("1ippReadIO: endCollection tag...");
|
|
return (ipp->state = IPP_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_INTERNAL_ERROR,
|
|
_("IPP memberName with no attribute."), 1);
|
|
DEBUG_puts("1ippReadIO: Member name without attribute.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
else if (n == 0)
|
|
{
|
|
_cupsSetError(IPP_INTERNAL_ERROR,
|
|
_("IPP memberName value is empty."), 1);
|
|
DEBUG_puts("1ippReadIO: Empty member name value.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
else if ((*cb)(src, buffer, n) < n)
|
|
{
|
|
DEBUG_puts("1ippReadIO: Unable to read member name value.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
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;
|
|
|
|
default : /* Other unsupported values */
|
|
value->unknown.length = n;
|
|
if (n > 0)
|
|
{
|
|
if ((value->unknown.data = malloc(n)) == NULL)
|
|
{
|
|
_cupsSetHTTPError(HTTP_ERROR);
|
|
DEBUG_puts("1ippReadIO: Unable to allocate value");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
if ((*cb)(src, value->unknown.data, n) < n)
|
|
{
|
|
DEBUG_puts("1ippReadIO: Unable to read unsupported value.");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
}
|
|
else
|
|
value->unknown.data = NULL;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If blocking is disabled, stop here...
|
|
*/
|
|
|
|
if (!blocking)
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case IPP_DATA :
|
|
break;
|
|
|
|
default :
|
|
break; /* anti-compiler-warning-code */
|
|
}
|
|
|
|
DEBUG_printf(("1ippReadIO: returning ipp->state=%d.", ipp->state));
|
|
_cupsBufferRelease((char *)buffer);
|
|
|
|
return (ipp->state);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippSetBoolean()' - Set a boolean value in an attribute.
|
|
*
|
|
* The @code ipp@ parameter refers to the IPP message containing the attribute that was
|
|
* previously created using the @link ippNew@ or @link ippNewRequest@ 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
|
|
* @link ippGetCount(attr)@.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippSetBoolean(ipp_t *ipp, /* IO - 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 */
|
|
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BOOLEAN ||
|
|
element < 0 || element > (*attr)->num_values)
|
|
return (0);
|
|
|
|
/*
|
|
* Set the value and return...
|
|
*/
|
|
|
|
if ((value = ipp_set_value(ipp, attr, element)) != NULL)
|
|
value->boolean = boolvalue;
|
|
|
|
return (value != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippSetCollection()' - Set a collection value in an attribute.
|
|
*
|
|
* The @code ipp@ parameter refers to the IPP message containing the attribute that was
|
|
* previously created using the @link ippNew@ or @link ippNewRequest@ 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
|
|
* @link ippGetCount(attr)@.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippSetCollection(
|
|
ipp_t *ipp, /* IO - 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 */
|
|
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BEGIN_COLLECTION ||
|
|
element < 0 || element > (*attr)->num_values || !colvalue)
|
|
return (0);
|
|
|
|
/*
|
|
* Set the value and return...
|
|
*/
|
|
|
|
if ((value = ipp_set_value(ipp, attr, element)) != NULL)
|
|
{
|
|
if (value->collection)
|
|
ippDelete(value->collection);
|
|
|
|
value->collection = colvalue;
|
|
colvalue->use ++;
|
|
}
|
|
|
|
return (value != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippSetDate()' - Set a date value in an attribute.
|
|
*
|
|
* The @code ipp@ parameter refers to the IPP message containing the attribute that was
|
|
* previously created using the @link ippNew@ or @link ippNewRequest@ 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
|
|
* @link ippGetCount(attr)@.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippSetDate(ipp_t *ipp, /* IO - IPP message */
|
|
ipp_attribute_t **attr, /* IO - IPP attribute */
|
|
int element, /* I - Value number (0-based) */
|
|
const ipp_uchar_t *datevalue)/* I - Date value */
|
|
{
|
|
_ipp_value_t *value; /* Current value */
|
|
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_DATE ||
|
|
element < 0 || element > (*attr)->num_values || !datevalue)
|
|
return (0);
|
|
|
|
/*
|
|
* Set the value and return...
|
|
*/
|
|
|
|
if ((value = ipp_set_value(ipp, attr, element)) != NULL)
|
|
memcpy(value->date, datevalue, sizeof(value->date));
|
|
|
|
return (value != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippSetGroupTag()' - Set the group tag of an attribute.
|
|
*
|
|
* The @code ipp@ parameter refers to the IPP message containing the attribute that was
|
|
* previously created using the @link ippNew@ or @link ippNewRequest@ 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/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippSetGroupTag(
|
|
ipp_t *ipp, /* IO - 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 2911...
|
|
*/
|
|
|
|
if (!ipp || !attr || group_tag < IPP_TAG_ZERO || group_tag == IPP_TAG_END ||
|
|
group_tag >= IPP_TAG_UNSUPPORTED_VALUE)
|
|
return (0);
|
|
|
|
/*
|
|
* Set the group tag and return...
|
|
*/
|
|
|
|
(*attr)->group_tag = group_tag;
|
|
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippSetInteger()' - Set an integer or enum value in an attribute.
|
|
*
|
|
* The @code ipp@ parameter refers to the IPP message containing the attribute that was
|
|
* previously created using the @link ippNew@ or @link ippNewRequest@ 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
|
|
* @link ippGetCount(attr)@.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippSetInteger(ipp_t *ipp, /* IO - 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) ||
|
|
element < 0 || element > (*attr)->num_values)
|
|
return (0);
|
|
|
|
/*
|
|
* Set the value and return...
|
|
*/
|
|
|
|
if ((value = ipp_set_value(ipp, attr, element)) != NULL)
|
|
value->integer = intvalue;
|
|
|
|
return (value != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippSetName()' - Set the name of an attribute.
|
|
*
|
|
* The @code ipp@ parameter refers to the IPP message containing the attribute that was
|
|
* previously created using the @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* The @code attr@ parameter may be modified as a result of setting the value.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippSetName(ipp_t *ipp, /* IO - IPP message */
|
|
ipp_attribute_t **attr, /* IO - IPP attribute */
|
|
const char *name) /* I - Attribute name */
|
|
{
|
|
char *temp; /* Temporary name value */
|
|
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !attr || !*attr)
|
|
return (0);
|
|
|
|
/*
|
|
* Set the value and return...
|
|
*/
|
|
|
|
if ((temp = _cupsStrAlloc(name)) != NULL)
|
|
{
|
|
if ((*attr)->name)
|
|
_cupsStrFree((*attr)->name);
|
|
|
|
(*attr)->name = temp;
|
|
}
|
|
|
|
return (temp != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* '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@ or @link ippNewRequest@ functions.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippSetOperation(ipp_t *ipp, /* I - IPP request message */
|
|
ipp_op_t op) /* I - Operation ID */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp)
|
|
return (0);
|
|
|
|
/*
|
|
* Set the operation and return...
|
|
*/
|
|
|
|
ipp->request.op.operation_id = op;
|
|
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippSetRange()' - Set a rangeOfInteger value in an attribute.
|
|
*
|
|
* The @code ipp@ parameter refers to the IPP message containing the attribute that was
|
|
* previously created using the @link ippNew@ or @link ippNewRequest@ 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
|
|
* @link ippGetCount(attr)@.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippSetRange(ipp_t *ipp, /* IO - 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 */
|
|
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_RANGE ||
|
|
element < 0 || element > (*attr)->num_values || lowervalue > uppervalue)
|
|
return (0);
|
|
|
|
/*
|
|
* Set the value and return...
|
|
*/
|
|
|
|
if ((value = ipp_set_value(ipp, attr, element)) != NULL)
|
|
{
|
|
value->range.lower = lowervalue;
|
|
value->range.upper = uppervalue;
|
|
}
|
|
|
|
return (value != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippSetRequestId()' - Set the request ID in an IPP message.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* The @code request_id@ parameter must be greater than 0.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippSetRequestId(ipp_t *ipp, /* I - IPP message */
|
|
int request_id) /* I - Request ID */
|
|
{
|
|
/*
|
|
* Range check input; not checking request_id values since ipptool wants to send
|
|
* invalid values for conformance testing and a bad request_id does not affect the
|
|
* encoding of a message...
|
|
*/
|
|
|
|
if (!ipp)
|
|
return (0);
|
|
|
|
/*
|
|
* Set the request ID and return...
|
|
*/
|
|
|
|
ipp->request.any.request_id = request_id;
|
|
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippSetResolution()' - Set a resolution value in an attribute.
|
|
*
|
|
* The @code ipp@ parameter refers to the IPP message containing the attribute that was
|
|
* previously created using the @link ippNew@ or @link ippNewRequest@ 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
|
|
* @link ippGetCount(attr)@.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippSetResolution(
|
|
ipp_t *ipp, /* IO - IPP message */
|
|
ipp_attribute_t **attr, /* IO - IPP attribute */
|
|
int element, /* I - Value number (0-based) */
|
|
ipp_res_t unitsvalue, /* I - Resolution units */
|
|
int xresvalue, /* I - Horizontal/cross feed resolution */
|
|
int yresvalue) /* I - Vertical/feed resolution */
|
|
{
|
|
_ipp_value_t *value; /* Current value */
|
|
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_RESOLUTION ||
|
|
element < 0 || element > (*attr)->num_values || xresvalue <= 0 || yresvalue <= 0 ||
|
|
unitsvalue < IPP_RES_PER_INCH || unitsvalue > IPP_RES_PER_CM)
|
|
return (0);
|
|
|
|
/*
|
|
* Set the value and return...
|
|
*/
|
|
|
|
if ((value = ipp_set_value(ipp, attr, element)) != NULL)
|
|
{
|
|
value->resolution.units = unitsvalue;
|
|
value->resolution.xres = xresvalue;
|
|
value->resolution.yres = yresvalue;
|
|
}
|
|
|
|
return (value != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippSetState()' - Set the current state of the IPP message.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippSetState(ipp_t *ipp, /* I - IPP message */
|
|
ipp_state_t state) /* I - IPP state value */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp)
|
|
return (0);
|
|
|
|
/*
|
|
* Set the state and return...
|
|
*/
|
|
|
|
ipp->state = state;
|
|
ipp->current = NULL;
|
|
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* '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@ or @link ippNewRequest@ functions.
|
|
*
|
|
* @since CUPS 1.6/OS X 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 */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp)
|
|
return (0);
|
|
|
|
/*
|
|
* Set the status code and return...
|
|
*/
|
|
|
|
ipp->request.status.status_code = status;
|
|
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippSetString()' - Set a string value in an attribute.
|
|
*
|
|
* The @code ipp@ parameter refers to the IPP message containing the attribute that was
|
|
* previously created using the @link ippNew@ or @link ippNewRequest@ 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
|
|
* @link ippGetCount(attr)@.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippSetString(ipp_t *ipp, /* IO - 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 */
|
|
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !attr || !*attr ||
|
|
((*attr)->value_tag != IPP_TAG_TEXTLANG &&
|
|
(*attr)->value_tag != IPP_TAG_NAMELANG &&
|
|
((*attr)->value_tag < IPP_TAG_TEXT ||
|
|
(*attr)->value_tag > IPP_TAG_MIMETYPE)) ||
|
|
element < 0 || element > (*attr)->num_values || !strvalue)
|
|
return (0);
|
|
|
|
/*
|
|
* Set the value and return...
|
|
*/
|
|
|
|
if ((value = ipp_set_value(ipp, attr, element)) != NULL)
|
|
{
|
|
if (element > 0)
|
|
value->string.language = (*attr)->values[0].string.language;
|
|
|
|
if ((int)((*attr)->value_tag) & IPP_TAG_COPY)
|
|
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);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippSetValueTag()' - Set the value tag of an attribute.
|
|
*
|
|
* The @code ipp@ parameter refers to the IPP message containing the attribute that was
|
|
* previously created using the @link ippNew@ or @link ippNewRequest@ 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/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippSetValueTag(
|
|
ipp_t *ipp, /* IO - 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 */
|
|
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || !attr)
|
|
return (0);
|
|
|
|
/*
|
|
* If there is no change, return immediately...
|
|
*/
|
|
|
|
if (value_tag == (*attr)->value_tag)
|
|
return (1);
|
|
|
|
/*
|
|
* Otherwise implement changes as needed...
|
|
*/
|
|
|
|
temp_tag = (ipp_tag_t)((int)((*attr)->value_tag) & IPP_TAG_MASK);
|
|
|
|
switch (value_tag)
|
|
{
|
|
case IPP_TAG_UNSUPPORTED_VALUE :
|
|
case IPP_TAG_DEFAULT :
|
|
case IPP_TAG_UNKNOWN :
|
|
case IPP_TAG_NOVALUE :
|
|
case IPP_TAG_NOTSETTABLE :
|
|
case IPP_TAG_DELETEATTR :
|
|
case IPP_TAG_ADMINDEFINE :
|
|
/*
|
|
* Free any existing values...
|
|
*/
|
|
|
|
if ((*attr)->num_values > 0)
|
|
ipp_free_values(*attr, 0, (*attr)->num_values);
|
|
|
|
/*
|
|
* Set out-of-band value...
|
|
*/
|
|
|
|
(*attr)->value_tag = value_tag;
|
|
break;
|
|
|
|
case IPP_TAG_RANGE :
|
|
if (temp_tag != IPP_TAG_INTEGER)
|
|
return (0);
|
|
|
|
for (i = (*attr)->num_values, value = (*attr)->values;
|
|
i > 0;
|
|
i --, value ++)
|
|
{
|
|
integer = value->integer;
|
|
value->range.lower = value->range.upper = integer;
|
|
}
|
|
|
|
(*attr)->value_tag = IPP_TAG_RANGE;
|
|
break;
|
|
|
|
case IPP_TAG_NAME :
|
|
if (temp_tag != IPP_TAG_KEYWORD && temp_tag != IPP_TAG_URI &&
|
|
temp_tag != IPP_TAG_URISCHEME && temp_tag != IPP_TAG_LANGUAGE &&
|
|
temp_tag != IPP_TAG_MIMETYPE)
|
|
return (0);
|
|
|
|
(*attr)->value_tag = (ipp_tag_t)(IPP_TAG_NAME | ((*attr)->value_tag & IPP_TAG_COPY));
|
|
break;
|
|
|
|
case IPP_TAG_NAMELANG :
|
|
case IPP_TAG_TEXTLANG :
|
|
if (value_tag == IPP_TAG_NAMELANG &&
|
|
(temp_tag != IPP_TAG_NAME && temp_tag != IPP_TAG_KEYWORD &&
|
|
temp_tag != IPP_TAG_URI && temp_tag != IPP_TAG_URISCHEME &&
|
|
temp_tag != IPP_TAG_LANGUAGE && temp_tag != IPP_TAG_MIMETYPE))
|
|
return (0);
|
|
|
|
if (value_tag == IPP_TAG_TEXTLANG && temp_tag != IPP_TAG_TEXT)
|
|
return (0);
|
|
|
|
if (ipp->attrs && ipp->attrs->next && ipp->attrs->next->name &&
|
|
!strcmp(ipp->attrs->next->name, "attributes-natural-language"))
|
|
{
|
|
/*
|
|
* Use the language code from the IPP message...
|
|
*/
|
|
|
|
(*attr)->values[0].string.language =
|
|
_cupsStrAlloc(ipp->attrs->next->values[0].string.text);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Otherwise, use the language code corresponding to the locale...
|
|
*/
|
|
|
|
language = cupsLangDefault();
|
|
(*attr)->values[0].string.language = _cupsStrAlloc(ipp_lang_code(language->language,
|
|
code,
|
|
sizeof(code)));
|
|
}
|
|
|
|
for (i = (*attr)->num_values - 1, value = (*attr)->values + 1;
|
|
i > 0;
|
|
i --, value ++)
|
|
value->string.language = (*attr)->values[0].string.language;
|
|
|
|
if ((int)(*attr)->value_tag & IPP_TAG_COPY)
|
|
{
|
|
/*
|
|
* Make copies of all values...
|
|
*/
|
|
|
|
for (i = (*attr)->num_values, value = (*attr)->values;
|
|
i > 0;
|
|
i --, value ++)
|
|
value->string.text = _cupsStrAlloc(value->string.text);
|
|
}
|
|
|
|
(*attr)->value_tag = IPP_TAG_NAMELANG;
|
|
break;
|
|
|
|
case IPP_TAG_KEYWORD :
|
|
if (temp_tag == IPP_TAG_NAME || temp_tag == IPP_TAG_NAMELANG)
|
|
break; /* Silently "allow" name -> keyword */
|
|
|
|
default :
|
|
return (0);
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippSetVersion()' - Set the version number in an IPP message.
|
|
*
|
|
* The @code ipp@ parameter refers to an IPP message previously created using the
|
|
* @link ippNew@ or @link ippNewRequest@ functions.
|
|
*
|
|
* The valid version numbers are currently 1.0, 1.1, 2.0, 2.1, and 2.2.
|
|
*
|
|
* @since CUPS 1.6/OS X 10.8@
|
|
*/
|
|
|
|
int /* O - 1 on success, 0 on failure */
|
|
ippSetVersion(ipp_t *ipp, /* I - IPP message */
|
|
int major, /* I - Major version number (major.minor) */
|
|
int minor) /* I - Minor version number (major.minor) */
|
|
{
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || major < 0 || minor < 0)
|
|
return (0);
|
|
|
|
/*
|
|
* Set the version number...
|
|
*/
|
|
|
|
ipp->request.any.version[0] = major;
|
|
ipp->request.any.version[1] = minor;
|
|
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippTimeToDate()' - Convert from UNIX time to RFC 1903 format.
|
|
*/
|
|
|
|
const ipp_uchar_t * /* O - RFC-1903 date/time data */
|
|
ippTimeToDate(time_t t) /* I - UNIX time value */
|
|
{
|
|
struct tm *unixdate; /* UNIX unixdate/time info */
|
|
ipp_uchar_t *date = _cupsGlobals()->ipp_date;
|
|
/* RFC-1903 date/time data */
|
|
|
|
|
|
/*
|
|
* RFC-1903 date/time format is:
|
|
*
|
|
* Byte(s) Description
|
|
* ------- -----------
|
|
* 0-1 Year (0 to 65535)
|
|
* 2 Month (1 to 12)
|
|
* 3 Day (1 to 31)
|
|
* 4 Hours (0 to 23)
|
|
* 5 Minutes (0 to 59)
|
|
* 6 Seconds (0 to 60, 60 = "leap second")
|
|
* 7 Deciseconds (0 to 9)
|
|
* 8 +/- UTC
|
|
* 9 UTC hours (0 to 11)
|
|
* 10 UTC minutes (0 to 59)
|
|
*/
|
|
|
|
unixdate = gmtime(&t);
|
|
unixdate->tm_year += 1900;
|
|
|
|
date[0] = unixdate->tm_year >> 8;
|
|
date[1] = unixdate->tm_year;
|
|
date[2] = unixdate->tm_mon + 1;
|
|
date[3] = unixdate->tm_mday;
|
|
date[4] = unixdate->tm_hour;
|
|
date[5] = unixdate->tm_min;
|
|
date[6] = unixdate->tm_sec;
|
|
date[7] = 0;
|
|
date[8] = '+';
|
|
date[9] = 0;
|
|
date[10] = 0;
|
|
|
|
return (date);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippWrite()' - Write data for an IPP message to a HTTP connection.
|
|
*/
|
|
|
|
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)", http, ipp));
|
|
|
|
if (!http)
|
|
return (IPP_ERROR);
|
|
|
|
return (ippWriteIO(http, (ipp_iocb_t)httpWrite2, http->blocking, NULL, ipp));
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippWriteFile()' - Write data for an IPP message to a file.
|
|
*
|
|
* @since CUPS 1.1.19/OS X 10.3@
|
|
*/
|
|
|
|
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, ipp));
|
|
|
|
ipp->state = IPP_IDLE;
|
|
|
|
return (ippWriteIO(&fd, (ipp_iocb_t)ipp_write_file, 1, NULL, ipp));
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ippWriteIO()' - Write data for an IPP message.
|
|
*
|
|
* @since CUPS 1.2/OS X 10.5@
|
|
*/
|
|
|
|
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 */
|
|
|
|
|
|
DEBUG_printf(("ippWriteIO(dst=%p, cb=%p, blocking=%d, parent=%p, ipp=%p)",
|
|
dst, cb, blocking, parent, ipp));
|
|
|
|
if (!dst || !ipp)
|
|
return (IPP_ERROR);
|
|
|
|
if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Unable to get write buffer");
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
switch (ipp->state)
|
|
{
|
|
case IPP_IDLE :
|
|
ipp->state ++; /* Avoid common problem... */
|
|
|
|
case IPP_HEADER :
|
|
if (parent == NULL)
|
|
{
|
|
/*
|
|
* Send the request header:
|
|
*
|
|
* Version = 2 bytes
|
|
* Operation/Status Code = 2 bytes
|
|
* Request ID = 4 bytes
|
|
* Total = 8 bytes
|
|
*/
|
|
|
|
bufptr = buffer;
|
|
|
|
*bufptr++ = ipp->request.any.version[0];
|
|
*bufptr++ = ipp->request.any.version[1];
|
|
*bufptr++ = ipp->request.any.op_status >> 8;
|
|
*bufptr++ = ipp->request.any.op_status;
|
|
*bufptr++ = ipp->request.any.request_id >> 24;
|
|
*bufptr++ = ipp->request.any.request_id >> 16;
|
|
*bufptr++ = ipp->request.any.request_id >> 8;
|
|
*bufptr++ = ipp->request.any.request_id;
|
|
|
|
DEBUG_printf(("2ippWriteIO: version=%d.%d", buffer[0], buffer[1]));
|
|
DEBUG_printf(("2ippWriteIO: op_status=%04x",
|
|
ipp->request.any.op_status));
|
|
DEBUG_printf(("2ippWriteIO: request_id=%d",
|
|
ipp->request.any.request_id));
|
|
|
|
if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP header...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reset the state engine to point to the first attribute
|
|
* in the request/response, with no current group.
|
|
*/
|
|
|
|
ipp->state = IPP_ATTRIBUTE;
|
|
ipp->current = ipp->attrs;
|
|
ipp->curtag = IPP_TAG_ZERO;
|
|
|
|
DEBUG_printf(("1ippWriteIO: ipp->current=%p", ipp->current));
|
|
|
|
/*
|
|
* If blocking is disabled, stop here...
|
|
*/
|
|
|
|
if (!blocking)
|
|
break;
|
|
|
|
case IPP_ATTRIBUTE :
|
|
while (ipp->current != NULL)
|
|
{
|
|
/*
|
|
* Write this attribute...
|
|
*/
|
|
|
|
bufptr = buffer;
|
|
attr = ipp->current;
|
|
|
|
ipp->current = ipp->current->next;
|
|
|
|
if (!parent)
|
|
{
|
|
if (ipp->curtag != attr->group_tag)
|
|
{
|
|
/*
|
|
* Send a group tag byte...
|
|
*/
|
|
|
|
ipp->curtag = attr->group_tag;
|
|
|
|
if (attr->group_tag == IPP_TAG_ZERO)
|
|
continue;
|
|
|
|
DEBUG_printf(("2ippWriteIO: wrote group tag=%x(%s)",
|
|
attr->group_tag, ippTagString(attr->group_tag)));
|
|
*bufptr++ = attr->group_tag;
|
|
}
|
|
else if (attr->group_tag == IPP_TAG_ZERO)
|
|
continue;
|
|
}
|
|
|
|
DEBUG_printf(("1ippWriteIO: %s (%s%s)", attr->name,
|
|
attr->num_values > 1 ? "1setOf " : "",
|
|
ippTagString(attr->value_tag)));
|
|
|
|
/*
|
|
* Write the attribute tag and name.
|
|
*
|
|
* The attribute name length does not include the trailing nul
|
|
* character in the source string.
|
|
*
|
|
* Collection values (parent != NULL) are written differently...
|
|
*/
|
|
|
|
if (parent == NULL)
|
|
{
|
|
/*
|
|
* Get the length of the attribute name, and make sure it won't
|
|
* overflow the buffer...
|
|
*/
|
|
|
|
if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 8))
|
|
{
|
|
DEBUG_printf(("1ippWriteIO: Attribute name too long (%d)", n));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
/*
|
|
* Write the value tag, name length, and name string...
|
|
*/
|
|
|
|
DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
|
|
attr->value_tag, ippTagString(attr->value_tag)));
|
|
DEBUG_printf(("2ippWriteIO: writing name=%d,\"%s\"", n,
|
|
attr->name));
|
|
|
|
if (attr->value_tag > 0xff)
|
|
{
|
|
*bufptr++ = IPP_TAG_EXTENSION;
|
|
*bufptr++ = attr->value_tag >> 24;
|
|
*bufptr++ = attr->value_tag >> 16;
|
|
*bufptr++ = attr->value_tag >> 8;
|
|
*bufptr++ = attr->value_tag;
|
|
}
|
|
else
|
|
*bufptr++ = attr->value_tag;
|
|
|
|
*bufptr++ = n >> 8;
|
|
*bufptr++ = n;
|
|
memcpy(bufptr, attr->name, n);
|
|
bufptr += n;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Get the length of the attribute name, and make sure it won't
|
|
* overflow the buffer...
|
|
*/
|
|
|
|
if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 12))
|
|
{
|
|
DEBUG_printf(("1ippWriteIO: Attribute name too long (%d)", n));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
/*
|
|
* Write the member name tag, name length, name string, value tag,
|
|
* and empty name for the collection member attribute...
|
|
*/
|
|
|
|
DEBUG_printf(("2ippWriteIO: writing value tag=%x(memberName)",
|
|
IPP_TAG_MEMBERNAME));
|
|
DEBUG_printf(("2ippWriteIO: writing name=%d,\"%s\"", n,
|
|
attr->name));
|
|
DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
|
|
attr->value_tag, ippTagString(attr->value_tag)));
|
|
DEBUG_puts("2ippWriteIO: writing name=0,\"\"");
|
|
|
|
*bufptr++ = IPP_TAG_MEMBERNAME;
|
|
*bufptr++ = 0;
|
|
*bufptr++ = 0;
|
|
*bufptr++ = n >> 8;
|
|
*bufptr++ = n;
|
|
memcpy(bufptr, attr->name, n);
|
|
bufptr += n;
|
|
|
|
if (attr->value_tag > 0xff)
|
|
{
|
|
*bufptr++ = IPP_TAG_EXTENSION;
|
|
*bufptr++ = attr->value_tag >> 24;
|
|
*bufptr++ = attr->value_tag >> 16;
|
|
*bufptr++ = attr->value_tag >> 8;
|
|
*bufptr++ = attr->value_tag;
|
|
}
|
|
else
|
|
*bufptr++ = attr->value_tag;
|
|
|
|
*bufptr++ = 0;
|
|
*bufptr++ = 0;
|
|
}
|
|
|
|
/*
|
|
* Now write the attribute value(s)...
|
|
*/
|
|
|
|
switch (attr->value_tag & ~IPP_TAG_COPY)
|
|
{
|
|
case IPP_TAG_UNSUPPORTED_VALUE :
|
|
case IPP_TAG_DEFAULT :
|
|
case IPP_TAG_UNKNOWN :
|
|
case IPP_TAG_NOVALUE :
|
|
case IPP_TAG_NOTSETTABLE :
|
|
case IPP_TAG_DELETEATTR :
|
|
case IPP_TAG_ADMINDEFINE :
|
|
*bufptr++ = 0;
|
|
*bufptr++ = 0;
|
|
break;
|
|
|
|
case IPP_TAG_INTEGER :
|
|
case IPP_TAG_ENUM :
|
|
for (i = 0, value = attr->values;
|
|
i < attr->num_values;
|
|
i ++, value ++)
|
|
{
|
|
if ((IPP_BUF_SIZE - (bufptr - buffer)) < 9)
|
|
{
|
|
if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP "
|
|
"attribute...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
bufptr = buffer;
|
|
}
|
|
|
|
if (i)
|
|
{
|
|
/*
|
|
* Arrays and sets are done by sending additional
|
|
* values with a zero-length name...
|
|
*/
|
|
|
|
*bufptr++ = 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++ = value->integer >> 24;
|
|
*bufptr++ = value->integer >> 16;
|
|
*bufptr++ = value->integer >> 8;
|
|
*bufptr++ = 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, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP "
|
|
"attribute...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
bufptr = buffer;
|
|
}
|
|
|
|
if (i)
|
|
{
|
|
/*
|
|
* Arrays and sets are done by sending additional
|
|
* values with a zero-length name...
|
|
*/
|
|
|
|
*bufptr++ = 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++ = 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, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP "
|
|
"attribute...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
bufptr = buffer;
|
|
}
|
|
|
|
*bufptr++ = 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_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, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP "
|
|
"attribute...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_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++ = n >> 8;
|
|
*bufptr++ = n;
|
|
|
|
if (n > 0)
|
|
{
|
|
memcpy(bufptr, value->string.text, 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, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP "
|
|
"attribute...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
bufptr = buffer;
|
|
}
|
|
|
|
if (i)
|
|
{
|
|
/*
|
|
* Arrays and sets are done by sending additional
|
|
* values with a zero-length name...
|
|
*/
|
|
|
|
*bufptr++ = 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, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP "
|
|
"attribute...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
bufptr = buffer;
|
|
}
|
|
|
|
if (i)
|
|
{
|
|
/*
|
|
* Arrays and sets are done by sending additional
|
|
* values with a zero-length name...
|
|
*/
|
|
|
|
*bufptr++ = 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++ = value->resolution.xres >> 24;
|
|
*bufptr++ = value->resolution.xres >> 16;
|
|
*bufptr++ = value->resolution.xres >> 8;
|
|
*bufptr++ = value->resolution.xres;
|
|
*bufptr++ = value->resolution.yres >> 24;
|
|
*bufptr++ = value->resolution.yres >> 16;
|
|
*bufptr++ = value->resolution.yres >> 8;
|
|
*bufptr++ = value->resolution.yres;
|
|
*bufptr++ = 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, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP "
|
|
"attribute...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
bufptr = buffer;
|
|
}
|
|
|
|
if (i)
|
|
{
|
|
/*
|
|
* Arrays and sets are done by sending additional
|
|
* values with a zero-length name...
|
|
*/
|
|
|
|
*bufptr++ = 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.
|
|
*/
|
|
|
|
*bufptr++ = 0;
|
|
*bufptr++ = 8;
|
|
*bufptr++ = value->range.lower >> 24;
|
|
*bufptr++ = value->range.lower >> 16;
|
|
*bufptr++ = value->range.lower >> 8;
|
|
*bufptr++ = value->range.lower;
|
|
*bufptr++ = value->range.upper >> 24;
|
|
*bufptr++ = value->range.upper >> 16;
|
|
*bufptr++ = value->range.upper >> 8;
|
|
*bufptr++ = value->range.upper;
|
|
}
|
|
break;
|
|
|
|
case IPP_TAG_TEXTLANG :
|
|
case IPP_TAG_NAMELANG :
|
|
for (i = 0, value = attr->values;
|
|
i < attr->num_values;
|
|
i ++, value ++)
|
|
{
|
|
if (i)
|
|
{
|
|
/*
|
|
* Arrays and sets are done by sending additional
|
|
* values with a zero-length name...
|
|
*/
|
|
|
|
if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
|
|
{
|
|
if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP "
|
|
"attribute...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
bufptr = buffer;
|
|
}
|
|
|
|
*bufptr++ = attr->value_tag;
|
|
*bufptr++ = 0;
|
|
*bufptr++ = 0;
|
|
}
|
|
|
|
/*
|
|
* textWithLanguage and nameWithLanguage values consist
|
|
* of a 2-byte length for both strings and their
|
|
* individual lengths, a 2-byte length for the
|
|
* character string, the character string without the
|
|
* trailing nul, a 2-byte length for the character
|
|
* set string, and the character set string without
|
|
* the trailing nul.
|
|
*/
|
|
|
|
n = 4;
|
|
|
|
if (value->string.language != NULL)
|
|
n += (int)strlen(value->string.language);
|
|
|
|
if (value->string.text != NULL)
|
|
n += (int)strlen(value->string.text);
|
|
|
|
if (n > (IPP_BUF_SIZE - 2))
|
|
{
|
|
DEBUG_printf(("1ippWriteIO: text/nameWithLanguage value "
|
|
"too long (%d)", n));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
|
|
{
|
|
if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP "
|
|
"attribute...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
bufptr = buffer;
|
|
}
|
|
|
|
/* Length of entire value */
|
|
*bufptr++ = n >> 8;
|
|
*bufptr++ = n;
|
|
|
|
/* Length of language */
|
|
if (value->string.language != NULL)
|
|
n = (int)strlen(value->string.language);
|
|
else
|
|
n = 0;
|
|
|
|
*bufptr++ = n >> 8;
|
|
*bufptr++ = n;
|
|
|
|
/* Language */
|
|
if (n > 0)
|
|
{
|
|
memcpy(bufptr, value->string.language, n);
|
|
bufptr += n;
|
|
}
|
|
|
|
/* Length of text */
|
|
if (value->string.text != NULL)
|
|
n = (int)strlen(value->string.text);
|
|
else
|
|
n = 0;
|
|
|
|
*bufptr++ = n >> 8;
|
|
*bufptr++ = n;
|
|
|
|
/* Text */
|
|
if (n > 0)
|
|
{
|
|
memcpy(bufptr, value->string.text, 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...
|
|
*/
|
|
|
|
if ((IPP_BUF_SIZE - (bufptr - buffer)) < 5)
|
|
{
|
|
if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP "
|
|
"attribute...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
bufptr = buffer;
|
|
}
|
|
|
|
if (i)
|
|
{
|
|
/*
|
|
* Arrays and sets are done by sending additional
|
|
* values with a zero-length name...
|
|
*/
|
|
|
|
*bufptr++ = attr->value_tag;
|
|
*bufptr++ = 0;
|
|
*bufptr++ = 0;
|
|
}
|
|
|
|
/*
|
|
* Write a data length of 0 and flush the buffer...
|
|
*/
|
|
|
|
*bufptr++ = 0;
|
|
*bufptr++ = 0;
|
|
|
|
if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP "
|
|
"attribute...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
bufptr = buffer;
|
|
|
|
/*
|
|
* Then write the collection attribute...
|
|
*/
|
|
|
|
value->collection->state = IPP_IDLE;
|
|
|
|
if (ippWriteIO(dst, cb, 1, ipp,
|
|
value->collection) == IPP_ERROR)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Unable to write collection value");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default :
|
|
for (i = 0, value = attr->values;
|
|
i < attr->num_values;
|
|
i ++, value ++)
|
|
{
|
|
if (i)
|
|
{
|
|
/*
|
|
* Arrays and sets are done by sending additional
|
|
* values with a zero-length name...
|
|
*/
|
|
|
|
if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
|
|
{
|
|
if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP "
|
|
"attribute...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
bufptr = buffer;
|
|
}
|
|
|
|
*bufptr++ = attr->value_tag;
|
|
*bufptr++ = 0;
|
|
*bufptr++ = 0;
|
|
}
|
|
|
|
/*
|
|
* An unknown value might some new value that a
|
|
* vendor has come up with. It consists of a
|
|
* 2-byte length and the bytes in the unknown
|
|
* value buffer.
|
|
*/
|
|
|
|
n = value->unknown.length;
|
|
|
|
if (n > (IPP_BUF_SIZE - 2))
|
|
{
|
|
DEBUG_printf(("1ippWriteIO: Data length too long (%d)",
|
|
n));
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
|
|
{
|
|
if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP "
|
|
"attribute...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
bufptr = buffer;
|
|
}
|
|
|
|
/* Length of unknown value */
|
|
*bufptr++ = n >> 8;
|
|
*bufptr++ = n;
|
|
|
|
/* Value */
|
|
if (n > 0)
|
|
{
|
|
memcpy(bufptr, value->unknown.data, n);
|
|
bufptr += n;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Write the data out...
|
|
*/
|
|
|
|
if (bufptr > buffer)
|
|
{
|
|
if ((*cb)(dst, buffer, (int)(bufptr - buffer)) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP attribute...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
DEBUG_printf(("2ippWriteIO: wrote %d bytes",
|
|
(int)(bufptr - buffer)));
|
|
}
|
|
|
|
/*
|
|
* If blocking is disabled, stop here...
|
|
*/
|
|
|
|
if (!blocking)
|
|
break;
|
|
}
|
|
|
|
if (ipp->current == NULL)
|
|
{
|
|
/*
|
|
* Done with all of the attributes; add the end-of-attributes
|
|
* tag or end-collection attribute...
|
|
*/
|
|
|
|
if (parent == NULL)
|
|
{
|
|
buffer[0] = IPP_TAG_END;
|
|
n = 1;
|
|
}
|
|
else
|
|
{
|
|
buffer[0] = IPP_TAG_END_COLLECTION;
|
|
buffer[1] = 0; /* empty name */
|
|
buffer[2] = 0;
|
|
buffer[3] = 0; /* empty value */
|
|
buffer[4] = 0;
|
|
n = 5;
|
|
}
|
|
|
|
if ((*cb)(dst, buffer, n) < 0)
|
|
{
|
|
DEBUG_puts("1ippWriteIO: Could not write IPP end-tag...");
|
|
_cupsBufferRelease((char *)buffer);
|
|
return (IPP_ERROR);
|
|
}
|
|
|
|
ipp->state = IPP_DATA;
|
|
}
|
|
break;
|
|
|
|
case IPP_DATA :
|
|
break;
|
|
|
|
default :
|
|
break; /* anti-compiler-warning-code */
|
|
}
|
|
|
|
_cupsBufferRelease((char *)buffer);
|
|
|
|
return (ipp->state);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ipp_add_attr()' - Add a new attribute to the message.
|
|
*/
|
|
|
|
static ipp_attribute_t * /* O - New attribute */
|
|
ipp_add_attr(ipp_t *ipp, /* I - IPP message */
|
|
const char *name, /* I - Attribute name or NULL */
|
|
ipp_tag_t group_tag, /* I - Group tag or IPP_TAG_ZERO */
|
|
ipp_tag_t value_tag, /* I - Value tag or IPP_TAG_ZERO */
|
|
int num_values) /* I - Number of values */
|
|
{
|
|
int alloc_values; /* Number of values to allocate */
|
|
ipp_attribute_t *attr; /* New attribute */
|
|
|
|
|
|
DEBUG_printf(("4ipp_add_attr(ipp=%p, name=\"%s\", group_tag=0x%x, value_tag=0x%x, "
|
|
"num_values=%d)", ipp, name, group_tag, value_tag, num_values));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!ipp || num_values < 0)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Allocate memory, rounding the allocation up as needed...
|
|
*/
|
|
|
|
if (num_values <= 1)
|
|
alloc_values = 1;
|
|
else
|
|
alloc_values = (num_values + IPP_MAX_VALUES - 1) & ~(IPP_MAX_VALUES - 1);
|
|
|
|
attr = calloc(sizeof(ipp_attribute_t) +
|
|
(alloc_values - 1) * sizeof(_ipp_value_t), 1);
|
|
|
|
if (attr)
|
|
{
|
|
/*
|
|
* Initialize attribute...
|
|
*/
|
|
|
|
if (name)
|
|
attr->name = _cupsStrAlloc(name);
|
|
|
|
attr->group_tag = group_tag;
|
|
attr->value_tag = value_tag;
|
|
attr->num_values = num_values;
|
|
|
|
/*
|
|
* Add it to the end of the linked list...
|
|
*/
|
|
|
|
if (ipp->last)
|
|
ipp->last->next = attr;
|
|
else
|
|
ipp->attrs = attr;
|
|
|
|
ipp->prev = ipp->last;
|
|
ipp->last = ipp->current = attr;
|
|
}
|
|
|
|
DEBUG_printf(("5ipp_add_attr: Returning %p", attr));
|
|
|
|
return (attr);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ipp_free_values()' - Free attribute values.
|
|
*/
|
|
|
|
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 */
|
|
|
|
|
|
DEBUG_printf(("4ipp_free_values(attr=%p, element=%d, count=%d)", attr,
|
|
element, count));
|
|
|
|
if (!(attr->value_tag & IPP_TAG_COPY))
|
|
{
|
|
/*
|
|
* Free values as needed...
|
|
*/
|
|
|
|
switch (attr->value_tag)
|
|
{
|
|
case IPP_TAG_TEXTLANG :
|
|
case IPP_TAG_NAMELANG :
|
|
if (element == 0 && count == attr->num_values &&
|
|
attr->values[0].string.language)
|
|
{
|
|
_cupsStrFree(attr->values[0].string.language);
|
|
attr->values[0].string.language = NULL;
|
|
}
|
|
/* Fall through to other string values */
|
|
|
|
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_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;
|
|
|
|
case IPP_TAG_BEGIN_COLLECTION :
|
|
for (i = count, value = attr->values + element;
|
|
i > 0;
|
|
i --, value ++)
|
|
{
|
|
ippDelete(value->collection);
|
|
value->collection = NULL;
|
|
}
|
|
break;
|
|
|
|
case IPP_TAG_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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we are not freeing values from the end, move the remaining values up...
|
|
*/
|
|
|
|
if ((element + count) < attr->num_values)
|
|
memmove(attr->values + element, attr->values + element + count,
|
|
(attr->num_values - count - element) * sizeof(_ipp_value_t));
|
|
|
|
attr->num_values -= count;
|
|
}
|
|
|
|
|
|
/*
|
|
* '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.
|
|
*/
|
|
|
|
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 */
|
|
|
|
|
|
/*
|
|
* Convert values to lowercase and change _ to - as needed...
|
|
*/
|
|
|
|
for (bufptr = buffer, bufend = buffer + bufsize - 1;
|
|
*value && bufptr < bufend;
|
|
value ++)
|
|
if (*value == '_')
|
|
*bufptr++ = '-';
|
|
else
|
|
*bufptr++ = _cups_tolower(*value);
|
|
|
|
*bufptr = '\0';
|
|
|
|
/*
|
|
* Return the converted string...
|
|
*/
|
|
|
|
return (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".
|
|
*/
|
|
|
|
static char * /* O - Language code string */
|
|
ipp_lang_code(const char *locale, /* I - Locale string */
|
|
char *buffer, /* I - String buffer */
|
|
size_t bufsize) /* I - Size of string buffer */
|
|
{
|
|
/*
|
|
* Map POSIX ("C") locale to generic English, otherwise convert the locale string as-is.
|
|
*/
|
|
|
|
if (!_cups_strcasecmp(locale, "c"))
|
|
{
|
|
strlcpy(buffer, "en", bufsize);
|
|
return (buffer);
|
|
}
|
|
else
|
|
return (ipp_get_code(locale, buffer, bufsize));
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ipp_length()' - Compute the length of an IPP message or collection value.
|
|
*/
|
|
|
|
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 */
|
|
|
|
|
|
DEBUG_printf(("3ipp_length(ipp=%p, collection=%d)", ipp, collection));
|
|
|
|
if (!ipp)
|
|
{
|
|
DEBUG_puts("4ipp_length: Returning 0 bytes");
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Start with 8 bytes for the IPP message header...
|
|
*/
|
|
|
|
bytes = collection ? 0 : 8;
|
|
|
|
/*
|
|
* Then add the lengths of each attribute...
|
|
*/
|
|
|
|
group = IPP_TAG_ZERO;
|
|
|
|
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;
|
|
|
|
bytes ++; /* Group tag */
|
|
}
|
|
|
|
if (!attr->name)
|
|
continue;
|
|
|
|
DEBUG_printf(("5ipp_length: attr->name=\"%s\", attr->num_values=%d, "
|
|
"bytes=" CUPS_LLFMT, attr->name, attr->num_values, CUPS_LLCAST bytes));
|
|
|
|
if (attr->value_tag < IPP_TAG_EXTENSION)
|
|
bytes += attr->num_values; /* Value tag for each value */
|
|
else
|
|
bytes += 5 * attr->num_values; /* Value tag for each value */
|
|
bytes += 2 * attr->num_values; /* Name lengths */
|
|
bytes += (int)strlen(attr->name); /* Name */
|
|
bytes += 2 * attr->num_values; /* Value lengths */
|
|
|
|
if (collection)
|
|
bytes += 5; /* Add membername overhead */
|
|
|
|
switch (attr->value_tag & ~IPP_TAG_COPY)
|
|
{
|
|
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;
|
|
|
|
case IPP_TAG_INTEGER :
|
|
case IPP_TAG_ENUM :
|
|
bytes += 4 * attr->num_values;
|
|
break;
|
|
|
|
case IPP_TAG_BOOLEAN :
|
|
bytes += attr->num_values;
|
|
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 (value->string.text)
|
|
bytes += strlen(value->string.text);
|
|
break;
|
|
|
|
case IPP_TAG_DATE :
|
|
bytes += 11 * attr->num_values;
|
|
break;
|
|
|
|
case IPP_TAG_RESOLUTION :
|
|
bytes += 9 * attr->num_values;
|
|
break;
|
|
|
|
case IPP_TAG_RANGE :
|
|
bytes += 8 * attr->num_values;
|
|
break;
|
|
|
|
case IPP_TAG_TEXTLANG :
|
|
case IPP_TAG_NAMELANG :
|
|
bytes += 4 * attr->num_values;/* Charset + text length */
|
|
|
|
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)
|
|
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 += value->unknown.length;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Finally, add 1 byte for the "end of attributes" tag or 5 bytes
|
|
* for the "end of collection" tag and return...
|
|
*/
|
|
|
|
if (collection)
|
|
bytes += 5;
|
|
else
|
|
bytes ++;
|
|
|
|
DEBUG_printf(("4ipp_length: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST bytes));
|
|
|
|
return (bytes);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ipp_read_http()' - Semi-blocking read on a HTTP connection...
|
|
*/
|
|
|
|
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 */
|
|
{
|
|
int tbytes, /* Total bytes read */
|
|
bytes; /* Bytes read this pass */
|
|
|
|
|
|
DEBUG_printf(("7ipp_read_http(http=%p, buffer=%p, length=%d)",
|
|
http, buffer, (int)length));
|
|
|
|
/*
|
|
* Loop until all bytes are read...
|
|
*/
|
|
|
|
for (tbytes = 0, bytes = 0;
|
|
tbytes < (int)length;
|
|
tbytes += bytes, buffer += bytes)
|
|
{
|
|
DEBUG_printf(("9ipp_read_http: tbytes=%d, http->state=%d", tbytes,
|
|
http->state));
|
|
|
|
if (http->state == HTTP_WAITING)
|
|
break;
|
|
|
|
if (http->used == 0 && !http->blocking)
|
|
{
|
|
/*
|
|
* Wait up to 10 seconds for more data on non-blocking sockets...
|
|
*/
|
|
|
|
if (!httpWait(http, 10000))
|
|
{
|
|
/*
|
|
* Signal no data...
|
|
*/
|
|
|
|
bytes = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((bytes = httpRead2(http, (char *)buffer, length - tbytes)) < 0)
|
|
{
|
|
#ifdef WIN32
|
|
break;
|
|
#else
|
|
if (errno != EAGAIN && errno != EINTR)
|
|
break;
|
|
|
|
bytes = 0;
|
|
#endif /* WIN32 */
|
|
}
|
|
else if (bytes == 0)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Return the number of bytes read...
|
|
*/
|
|
|
|
if (tbytes == 0 && bytes < 0)
|
|
tbytes = -1;
|
|
|
|
DEBUG_printf(("8ipp_read_http: Returning %d bytes", tbytes));
|
|
|
|
return (tbytes);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ipp_read_file()' - Read IPP data from a file.
|
|
*/
|
|
|
|
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 */
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ipp_set_value()' - Get the value element from an attribute, expanding it as
|
|
* needed.
|
|
*/
|
|
|
|
static _ipp_value_t * /* O - IPP value element or NULL on error */
|
|
ipp_set_value(ipp_t *ipp, /* IO - IPP message */
|
|
ipp_attribute_t **attr, /* IO - IPP attribute */
|
|
int element) /* I - Value number (0-based) */
|
|
{
|
|
ipp_attribute_t *temp, /* New attribute pointer */
|
|
*current, /* Current attribute in list */
|
|
*prev; /* Previous attribute in list */
|
|
int alloc_values; /* Allocated values */
|
|
|
|
|
|
/*
|
|
* If we are setting an existing value element, return it...
|
|
*/
|
|
|
|
temp = *attr;
|
|
|
|
if (temp->num_values <= 1)
|
|
alloc_values = 1;
|
|
else
|
|
alloc_values = (temp->num_values + IPP_MAX_VALUES - 1) &
|
|
~(IPP_MAX_VALUES - 1);
|
|
|
|
if (element < alloc_values)
|
|
{
|
|
if (element >= temp->num_values)
|
|
temp->num_values = element + 1;
|
|
|
|
return (temp->values + element);
|
|
}
|
|
|
|
/*
|
|
* Otherwise re-allocate the attribute - we allocate in groups of IPP_MAX_VALUE
|
|
* values when num_values > 1.
|
|
*/
|
|
|
|
if (alloc_values < IPP_MAX_VALUES)
|
|
alloc_values = IPP_MAX_VALUES;
|
|
else
|
|
alloc_values += IPP_MAX_VALUES;
|
|
|
|
DEBUG_printf(("4ipp_set_value: Reallocating for up to %d values.",
|
|
alloc_values));
|
|
|
|
/*
|
|
* Reallocate memory...
|
|
*/
|
|
|
|
if ((temp = realloc(temp, sizeof(ipp_attribute_t) +
|
|
(alloc_values - 1) * sizeof(_ipp_value_t))) == NULL)
|
|
{
|
|
_cupsSetHTTPError(HTTP_ERROR);
|
|
DEBUG_puts("4ipp_set_value: Unable to resize attribute.");
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Zero the new memory...
|
|
*/
|
|
|
|
memset(temp->values + temp->num_values, 0,
|
|
(alloc_values - temp->num_values) * sizeof(_ipp_value_t));
|
|
|
|
if (temp != *attr)
|
|
{
|
|
/*
|
|
* Reset pointers in the list...
|
|
*/
|
|
|
|
if (ipp->current == *attr && ipp->prev)
|
|
{
|
|
/*
|
|
* Use current "previous" pointer...
|
|
*/
|
|
|
|
prev = ipp->prev;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Find this attribute in the linked list...
|
|
*/
|
|
|
|
for (prev = NULL, current = ipp->attrs;
|
|
current && current != *attr;
|
|
prev = current, current = current->next);
|
|
|
|
if (!current)
|
|
{
|
|
/*
|
|
* This is a serious error!
|
|
*/
|
|
|
|
*attr = temp;
|
|
_cupsSetError(IPP_INTERNAL_ERROR,
|
|
_("IPP attribute is not a member of the message."), 1);
|
|
DEBUG_puts("4ipp_set_value: Unable to find attribute in message.");
|
|
return (NULL);
|
|
}
|
|
}
|
|
|
|
if (prev)
|
|
prev->next = temp;
|
|
else
|
|
ipp->attrs = temp;
|
|
|
|
ipp->current = temp;
|
|
ipp->prev = prev;
|
|
|
|
if (ipp->last == *attr)
|
|
ipp->last = temp;
|
|
|
|
*attr = temp;
|
|
}
|
|
|
|
/*
|
|
* Return the value element...
|
|
*/
|
|
|
|
if (element >= temp->num_values)
|
|
temp->num_values = element + 1;
|
|
|
|
return (temp->values + element);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'ipp_write_file()' - Write IPP data to a file.
|
|
*/
|
|
|
|
static ssize_t /* O - Number of bytes written */
|
|
ipp_write_file(int *fd, /* I - File descriptor */
|
|
ipp_uchar_t *buffer, /* I - Data to write */
|
|
size_t length) /* I - Number of bytes to write */
|
|
{
|
|
#ifdef WIN32
|
|
return ((ssize_t)write(*fd, buffer, (unsigned)length));
|
|
#else
|
|
return (write(*fd, buffer, length));
|
|
#endif /* WIN32 */
|
|
}
|
|
|
|
|
|
/*
|
|
* End of "$Id: ipp.c 10102 2011-11-02 23:52:39Z mike $".
|
|
*/
|