Home | History | Annotate | Download | only in cups
      1 /*
      2  * Internet Printing Protocol functions for CUPS.
      3  *
      4  * Copyright 2007-2017 by Apple Inc.
      5  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
      6  *
      7  * These coded instructions, statements, and computer programs are the
      8  * property of Apple Inc. and are protected by Federal copyright
      9  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
     10  * which should have been included with this file.  If this file is
     11  * missing or damaged, see the license at "http://www.cups.org/".
     12  *
     13  * This file is subject to the Apple OS-Developed Software exception.
     14  */
     15 
     16 /*
     17  * Include necessary headers...
     18  */
     19 
     20 #include "cups-private.h"
     21 #include <regex.h>
     22 #ifdef WIN32
     23 #  include <io.h>
     24 #endif /* WIN32 */
     25 
     26 
     27 /*
     28  * Local functions...
     29  */
     30 
     31 static ipp_attribute_t	*ipp_add_attr(ipp_t *ipp, const char *name,
     32 			              ipp_tag_t  group_tag, ipp_tag_t value_tag,
     33 			              int num_values);
     34 static void		ipp_free_values(ipp_attribute_t *attr, int element,
     35 			                int count);
     36 static char		*ipp_get_code(const char *locale, char *buffer,
     37 			              size_t bufsize)
     38 			              __attribute__((nonnull(1,2)));
     39 static char		*ipp_lang_code(const char *locale, char *buffer,
     40 			               size_t bufsize)
     41 			               __attribute__((nonnull(1,2)));
     42 static size_t		ipp_length(ipp_t *ipp, int collection);
     43 static ssize_t		ipp_read_http(http_t *http, ipp_uchar_t *buffer,
     44 			              size_t length);
     45 static ssize_t		ipp_read_file(int *fd, ipp_uchar_t *buffer,
     46 			              size_t length);
     47 static void		ipp_set_error(ipp_status_t status, const char *format,
     48 			              ...);
     49 static _ipp_value_t	*ipp_set_value(ipp_t *ipp, ipp_attribute_t **attr,
     50 			               int element);
     51 static ssize_t		ipp_write_file(int *fd, ipp_uchar_t *buffer,
     52 			               size_t length);
     53 
     54 
     55 /*
     56  * '_cupsBufferGet()' - Get a read/write buffer.
     57  */
     58 
     59 char *					/* O - Buffer */
     60 _cupsBufferGet(size_t size)		/* I - Size required */
     61 {
     62   _cups_buffer_t	*buffer;	/* Current buffer */
     63   _cups_globals_t	*cg = _cupsGlobals();
     64 					/* Global data */
     65 
     66 
     67   for (buffer = cg->cups_buffers; buffer; buffer = buffer->next)
     68     if (!buffer->used && buffer->size >= size)
     69       break;
     70 
     71   if (!buffer)
     72   {
     73     if ((buffer = malloc(sizeof(_cups_buffer_t) + size - 1)) == NULL)
     74       return (NULL);
     75 
     76     buffer->next     = cg->cups_buffers;
     77     buffer->size     = size;
     78     cg->cups_buffers = buffer;
     79   }
     80 
     81   buffer->used = 1;
     82 
     83   return (buffer->d);
     84 }
     85 
     86 
     87 /*
     88  * '_cupsBufferRelease()' - Release a read/write buffer.
     89  */
     90 
     91 void
     92 _cupsBufferRelease(char *b)		/* I - Buffer to release */
     93 {
     94   _cups_buffer_t	*buffer;	/* Buffer */
     95 
     96 
     97  /*
     98   * Mark this buffer as unused...
     99   */
    100 
    101   buffer       = (_cups_buffer_t *)(b - offsetof(_cups_buffer_t, d));
    102   buffer->used = 0;
    103 }
    104 
    105 
    106 /*
    107  * 'ippAddBoolean()' - Add a boolean attribute to an IPP message.
    108  *
    109  * The @code ipp@ parameter refers to an IPP message previously created using
    110  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    111  *
    112  * The @code group@ parameter specifies the IPP attribute group tag: none
    113  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
    114  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
    115  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
    116  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    117  */
    118 
    119 ipp_attribute_t *			/* O - New attribute */
    120 ippAddBoolean(ipp_t      *ipp,		/* I - IPP message */
    121               ipp_tag_t  group,		/* I - IPP group */
    122               const char *name,		/* I - Name of attribute */
    123               char       value)		/* I - Value of attribute */
    124 {
    125   ipp_attribute_t	*attr;		/* New attribute */
    126 
    127 
    128   DEBUG_printf(("ippAddBoolean(ipp=%p, group=%02x(%s), name=\"%s\", value=%d)", (void *)ipp, group, ippTagString(group), name, value));
    129 
    130  /*
    131   * Range check input...
    132   */
    133 
    134   if (!ipp || !name || group < IPP_TAG_ZERO ||
    135       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
    136     return (NULL);
    137 
    138  /*
    139   * Create the attribute...
    140   */
    141 
    142   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BOOLEAN, 1)) == NULL)
    143     return (NULL);
    144 
    145   attr->values[0].boolean = value;
    146 
    147   return (attr);
    148 }
    149 
    150 
    151 /*
    152  * 'ippAddBooleans()' - Add an array of boolean values.
    153  *
    154  * The @code ipp@ parameter refers to an IPP message previously created using
    155  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    156  *
    157  * The @code group@ parameter specifies the IPP attribute group tag: none
    158  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
    159  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
    160  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
    161  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    162  */
    163 
    164 ipp_attribute_t *			/* O - New attribute */
    165 ippAddBooleans(ipp_t      *ipp,		/* I - IPP message */
    166                ipp_tag_t  group,	/* I - IPP group */
    167 	       const char *name,	/* I - Name of attribute */
    168 	       int        num_values,	/* I - Number of values */
    169 	       const char *values)	/* I - Values */
    170 {
    171   int			i;		/* Looping var */
    172   ipp_attribute_t	*attr;		/* New attribute */
    173   _ipp_value_t		*value;		/* Current value */
    174 
    175 
    176   DEBUG_printf(("ippAddBooleans(ipp=%p, group=%02x(%s), name=\"%s\", num_values=%d, values=%p)", (void *)ipp, group, ippTagString(group), name, num_values, (void *)values));
    177 
    178  /*
    179   * Range check input...
    180   */
    181 
    182   if (!ipp || !name || group < IPP_TAG_ZERO ||
    183       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
    184       num_values < 1)
    185     return (NULL);
    186 
    187  /*
    188   * Create the attribute...
    189   */
    190 
    191   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BOOLEAN, num_values)) == NULL)
    192     return (NULL);
    193 
    194   if (values)
    195   {
    196     for (i = num_values, value = attr->values;
    197 	 i > 0;
    198 	 i --, value ++)
    199       value->boolean = *values++;
    200   }
    201 
    202   return (attr);
    203 }
    204 
    205 
    206 /*
    207  * 'ippAddCollection()' - Add a collection value.
    208  *
    209  * The @code ipp@ parameter refers to an IPP message previously created using
    210  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    211  *
    212  * The @code group@ parameter specifies the IPP attribute group tag: none
    213  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
    214  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
    215  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
    216  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    217  *
    218  * @since CUPS 1.1.19/macOS 10.3@
    219  */
    220 
    221 ipp_attribute_t *			/* O - New attribute */
    222 ippAddCollection(ipp_t      *ipp,	/* I - IPP message */
    223                  ipp_tag_t  group,	/* I - IPP group */
    224 		 const char *name,	/* I - Name of attribute */
    225 		 ipp_t      *value)	/* I - Value */
    226 {
    227   ipp_attribute_t	*attr;		/* New attribute */
    228 
    229 
    230   DEBUG_printf(("ippAddCollection(ipp=%p, group=%02x(%s), name=\"%s\", value=%p)", (void *)ipp, group, ippTagString(group), name, (void *)value));
    231 
    232  /*
    233   * Range check input...
    234   */
    235 
    236   if (!ipp || !name || group < IPP_TAG_ZERO ||
    237       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
    238     return (NULL);
    239 
    240  /*
    241   * Create the attribute...
    242   */
    243 
    244   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BEGIN_COLLECTION, 1)) == NULL)
    245     return (NULL);
    246 
    247   attr->values[0].collection = value;
    248 
    249   if (value)
    250     value->use ++;
    251 
    252   return (attr);
    253 }
    254 
    255 
    256 /*
    257  * 'ippAddCollections()' - Add an array of collection values.
    258  *
    259  * The @code ipp@ parameter refers to an IPP message previously created using
    260  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    261  *
    262  * The @code group@ parameter specifies the IPP attribute group tag: none
    263  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
    264  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
    265  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
    266  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    267  *
    268  * @since CUPS 1.1.19/macOS 10.3@
    269  */
    270 
    271 ipp_attribute_t *			/* O - New attribute */
    272 ippAddCollections(
    273     ipp_t       *ipp,			/* I - IPP message */
    274     ipp_tag_t   group,			/* I - IPP group */
    275     const char  *name,			/* I - Name of attribute */
    276     int         num_values,		/* I - Number of values */
    277     const ipp_t **values)		/* I - Values */
    278 {
    279   int			i;		/* Looping var */
    280   ipp_attribute_t	*attr;		/* New attribute */
    281   _ipp_value_t		*value;		/* Current value */
    282 
    283 
    284   DEBUG_printf(("ippAddCollections(ipp=%p, group=%02x(%s), name=\"%s\", num_values=%d, values=%p)", (void *)ipp, group, ippTagString(group), name, num_values, (void *)values));
    285 
    286  /*
    287   * Range check input...
    288   */
    289 
    290   if (!ipp || !name || group < IPP_TAG_ZERO ||
    291       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
    292       num_values < 1)
    293     return (NULL);
    294 
    295  /*
    296   * Create the attribute...
    297   */
    298 
    299   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BEGIN_COLLECTION,
    300                            num_values)) == NULL)
    301     return (NULL);
    302 
    303   if (values)
    304   {
    305     for (i = num_values, value = attr->values;
    306 	 i > 0;
    307 	 i --, value ++)
    308     {
    309       value->collection = (ipp_t *)*values++;
    310       value->collection->use ++;
    311     }
    312   }
    313 
    314   return (attr);
    315 }
    316 
    317 
    318 /*
    319  * 'ippAddDate()' - Add a dateTime attribute to an IPP message.
    320  *
    321  * The @code ipp@ parameter refers to an IPP message previously created using
    322  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    323  *
    324  * The @code group@ parameter specifies the IPP attribute group tag: none
    325  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
    326  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
    327  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
    328  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    329  */
    330 
    331 ipp_attribute_t *			/* O - New attribute */
    332 ippAddDate(ipp_t             *ipp,	/* I - IPP message */
    333            ipp_tag_t         group,	/* I - IPP group */
    334 	   const char        *name,	/* I - Name of attribute */
    335 	   const ipp_uchar_t *value)	/* I - Value */
    336 {
    337   ipp_attribute_t	*attr;		/* New attribute */
    338 
    339 
    340   DEBUG_printf(("ippAddDate(ipp=%p, group=%02x(%s), name=\"%s\", value=%p)", (void *)ipp, group, ippTagString(group), name, (void *)value));
    341 
    342  /*
    343   * Range check input...
    344   */
    345 
    346   if (!ipp || !name || !value || group < IPP_TAG_ZERO ||
    347       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
    348     return (NULL);
    349 
    350  /*
    351   * Create the attribute...
    352   */
    353 
    354   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_DATE, 1)) == NULL)
    355     return (NULL);
    356 
    357   memcpy(attr->values[0].date, value, 11);
    358 
    359   return (attr);
    360 }
    361 
    362 
    363 /*
    364  * 'ippAddInteger()' - Add a integer attribute to an IPP message.
    365  *
    366  * The @code ipp@ parameter refers to an IPP message previously created using
    367  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    368  *
    369  * The @code group@ parameter specifies the IPP attribute group tag: none
    370  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
    371  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
    372  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
    373  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    374  *
    375  * Supported values include enum (@code IPP_TAG_ENUM@) and integer
    376  * (@code IPP_TAG_INTEGER@).
    377  */
    378 
    379 ipp_attribute_t *			/* O - New attribute */
    380 ippAddInteger(ipp_t      *ipp,		/* I - IPP message */
    381               ipp_tag_t  group,		/* I - IPP group */
    382 	      ipp_tag_t  value_tag,	/* I - Type of attribute */
    383               const char *name,		/* I - Name of attribute */
    384               int        value)		/* I - Value of attribute */
    385 {
    386   ipp_attribute_t	*attr;		/* New attribute */
    387 
    388 
    389   DEBUG_printf(("ippAddInteger(ipp=%p, group=%02x(%s), type=%02x(%s), name=\"%s\", value=%d)", (void *)ipp, group, ippTagString(group), value_tag, ippTagString(value_tag), name, value));
    390 
    391   value_tag &= IPP_TAG_CUPS_MASK;
    392 
    393  /*
    394   * Special-case for legacy usage: map out-of-band attributes to new ippAddOutOfBand
    395   * function...
    396   */
    397 
    398   if (value_tag >= IPP_TAG_UNSUPPORTED_VALUE && value_tag <= IPP_TAG_ADMINDEFINE)
    399     return (ippAddOutOfBand(ipp, group, value_tag, name));
    400 
    401  /*
    402   * Range check input...
    403   */
    404 
    405 #if 0
    406   if (!ipp || !name || group < IPP_TAG_ZERO ||
    407       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
    408       (value_tag != IPP_TAG_INTEGER && value_tag != IPP_TAG_ENUM))
    409     return (NULL);
    410 #else
    411   if (!ipp || !name || group < IPP_TAG_ZERO ||
    412       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
    413     return (NULL);
    414 #endif /* 0 */
    415 
    416  /*
    417   * Create the attribute...
    418   */
    419 
    420   if ((attr = ipp_add_attr(ipp, name, group, value_tag, 1)) == NULL)
    421     return (NULL);
    422 
    423   attr->values[0].integer = value;
    424 
    425   return (attr);
    426 }
    427 
    428 
    429 /*
    430  * 'ippAddIntegers()' - Add an array of integer values.
    431  *
    432  * The @code ipp@ parameter refers to an IPP message previously created using
    433  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    434  *
    435  * The @code group@ parameter specifies the IPP attribute group tag: none
    436  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
    437  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
    438  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
    439  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    440  *
    441  * Supported values include enum (@code IPP_TAG_ENUM@) and integer
    442  * (@code IPP_TAG_INTEGER@).
    443  */
    444 
    445 ipp_attribute_t *			/* O - New attribute */
    446 ippAddIntegers(ipp_t      *ipp,		/* I - IPP message */
    447                ipp_tag_t  group,	/* I - IPP group */
    448 	       ipp_tag_t  value_tag,	/* I - Type of attribute */
    449 	       const char *name,	/* I - Name of attribute */
    450 	       int        num_values,	/* I - Number of values */
    451 	       const int  *values)	/* I - Values */
    452 {
    453   int			i;		/* Looping var */
    454   ipp_attribute_t	*attr;		/* New attribute */
    455   _ipp_value_t		*value;		/* Current value */
    456 
    457 
    458   DEBUG_printf(("ippAddIntegers(ipp=%p, group=%02x(%s), type=%02x(%s), name=\"%s\", num_values=%d, values=%p)", (void *)ipp, group, ippTagString(group), value_tag, ippTagString(value_tag), name, num_values, (void *)values));
    459 
    460   value_tag &= IPP_TAG_CUPS_MASK;
    461 
    462  /*
    463   * Range check input...
    464   */
    465 
    466 #if 0
    467   if (!ipp || !name || group < IPP_TAG_ZERO ||
    468       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
    469       (value_tag != IPP_TAG_INTEGER && value_tag != IPP_TAG_ENUM) ||
    470       num_values < 1)
    471     return (NULL);
    472 #else
    473   if (!ipp || !name || group < IPP_TAG_ZERO ||
    474       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
    475       num_values < 1)
    476     return (NULL);
    477 #endif /* 0 */
    478 
    479  /*
    480   * Create the attribute...
    481   */
    482 
    483   if ((attr = ipp_add_attr(ipp, name, group, value_tag, num_values)) == NULL)
    484     return (NULL);
    485 
    486   if (values)
    487   {
    488     for (i = num_values, value = attr->values;
    489 	 i > 0;
    490 	 i --, value ++)
    491       value->integer = *values++;
    492   }
    493 
    494   return (attr);
    495 }
    496 
    497 
    498 /*
    499  * 'ippAddOctetString()' - Add an octetString value to an IPP message.
    500  *
    501  * The @code ipp@ parameter refers to an IPP message previously created using
    502  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    503  *
    504  * The @code group@ parameter specifies the IPP attribute group tag: none
    505  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
    506  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
    507  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
    508  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    509  *
    510  * @since CUPS 1.2/macOS 10.5@
    511  */
    512 
    513 ipp_attribute_t	*			/* O - New attribute */
    514 ippAddOctetString(ipp_t      *ipp,	/* I - IPP message */
    515                   ipp_tag_t  group,	/* I - IPP group */
    516                   const char *name,	/* I - Name of attribute */
    517                   const void *data,	/* I - octetString data */
    518 		  int        datalen)	/* I - Length of data in bytes */
    519 {
    520   ipp_attribute_t	*attr;		/* New attribute */
    521 
    522 
    523   if (!ipp || !name || group < IPP_TAG_ZERO ||
    524       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
    525       datalen < 0 || datalen > IPP_MAX_LENGTH)
    526     return (NULL);
    527 
    528   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_STRING, 1)) == NULL)
    529     return (NULL);
    530 
    531  /*
    532   * Initialize the attribute data...
    533   */
    534 
    535   attr->values[0].unknown.length = datalen;
    536 
    537   if (data)
    538   {
    539     if ((attr->values[0].unknown.data = malloc((size_t)datalen)) == NULL)
    540     {
    541       ippDeleteAttribute(ipp, attr);
    542       return (NULL);
    543     }
    544 
    545     memcpy(attr->values[0].unknown.data, data, (size_t)datalen);
    546   }
    547 
    548  /*
    549   * Return the new attribute...
    550   */
    551 
    552   return (attr);
    553 }
    554 
    555 
    556 /*
    557  * 'ippAddOutOfBand()' - Add an out-of-band value to an IPP message.
    558  *
    559  * The @code ipp@ parameter refers to an IPP message previously created using
    560  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    561  *
    562  * The @code group@ parameter specifies the IPP attribute group tag: none
    563  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
    564  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
    565  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
    566  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    567  *
    568  * Supported out-of-band values include unsupported-value
    569  * (@code IPP_TAG_UNSUPPORTED_VALUE@), default (@code IPP_TAG_DEFAULT@), unknown
    570  * (@code IPP_TAG_UNKNOWN@), no-value (@code IPP_TAG_NOVALUE@), not-settable
    571  * (@code IPP_TAG_NOTSETTABLE@), delete-attribute (@code IPP_TAG_DELETEATTR@), and
    572  * admin-define (@code IPP_TAG_ADMINDEFINE@).
    573  *
    574  * @since CUPS 1.6/macOS 10.8@
    575  */
    576 
    577 ipp_attribute_t	*			/* O - New attribute */
    578 ippAddOutOfBand(ipp_t      *ipp,	/* I - IPP message */
    579                 ipp_tag_t  group,	/* I - IPP group */
    580                 ipp_tag_t  value_tag,	/* I - Type of attribute */
    581 		const char *name)	/* I - Name of attribute */
    582 {
    583   DEBUG_printf(("ippAddOutOfBand(ipp=%p, group=%02x(%s), value_tag=%02x(%s), name=\"%s\")", (void *)ipp, group, ippTagString(group), value_tag, ippTagString(value_tag), name));
    584 
    585   value_tag &= IPP_TAG_CUPS_MASK;
    586 
    587  /*
    588   * Range check input...
    589   */
    590 
    591   if (!ipp || !name || group < IPP_TAG_ZERO ||
    592       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
    593       (value_tag != IPP_TAG_UNSUPPORTED_VALUE &&
    594        value_tag != IPP_TAG_DEFAULT &&
    595        value_tag != IPP_TAG_UNKNOWN &&
    596        value_tag != IPP_TAG_NOVALUE &&
    597        value_tag != IPP_TAG_NOTSETTABLE &&
    598        value_tag != IPP_TAG_DELETEATTR &&
    599        value_tag != IPP_TAG_ADMINDEFINE))
    600     return (NULL);
    601 
    602  /*
    603   * Create the attribute...
    604   */
    605 
    606   return (ipp_add_attr(ipp, name, group, value_tag, 1));
    607 }
    608 
    609 
    610 /*
    611  * 'ippAddRange()' - Add a range of values to an IPP message.
    612  *
    613  * The @code ipp@ parameter refers to an IPP message previously created using
    614  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    615  *
    616  * The @code group@ parameter specifies the IPP attribute group tag: none
    617  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
    618  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
    619  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
    620  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    621  *
    622  * The @code lower@ parameter must be less than or equal to the @code upper@ parameter.
    623  */
    624 
    625 ipp_attribute_t *			/* O - New attribute */
    626 ippAddRange(ipp_t      *ipp,		/* I - IPP message */
    627             ipp_tag_t  group,		/* I - IPP group */
    628 	    const char *name,		/* I - Name of attribute */
    629 	    int        lower,		/* I - Lower value */
    630 	    int        upper)		/* I - Upper value */
    631 {
    632   ipp_attribute_t	*attr;		/* New attribute */
    633 
    634 
    635   DEBUG_printf(("ippAddRange(ipp=%p, group=%02x(%s), name=\"%s\", lower=%d, upper=%d)", (void *)ipp, group, ippTagString(group), name, lower, upper));
    636 
    637  /*
    638   * Range check input...
    639   */
    640 
    641   if (!ipp || !name || group < IPP_TAG_ZERO ||
    642       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
    643     return (NULL);
    644 
    645  /*
    646   * Create the attribute...
    647   */
    648 
    649   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RANGE, 1)) == NULL)
    650     return (NULL);
    651 
    652   attr->values[0].range.lower = lower;
    653   attr->values[0].range.upper = upper;
    654 
    655   return (attr);
    656 }
    657 
    658 
    659 /*
    660  * 'ippAddRanges()' - Add ranges of values to an IPP message.
    661  *
    662  * The @code ipp@ parameter refers to an IPP message previously created using
    663  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    664  *
    665  * The @code group@ parameter specifies the IPP attribute group tag: none
    666  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
    667  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
    668  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
    669  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    670  */
    671 
    672 ipp_attribute_t *			/* O - New attribute */
    673 ippAddRanges(ipp_t      *ipp,		/* I - IPP message */
    674              ipp_tag_t  group,		/* I - IPP group */
    675 	     const char *name,		/* I - Name of attribute */
    676 	     int        num_values,	/* I - Number of values */
    677 	     const int  *lower,		/* I - Lower values */
    678 	     const int  *upper)		/* I - Upper values */
    679 {
    680   int			i;		/* Looping var */
    681   ipp_attribute_t	*attr;		/* New attribute */
    682   _ipp_value_t		*value;		/* Current value */
    683 
    684 
    685   DEBUG_printf(("ippAddRanges(ipp=%p, group=%02x(%s), name=\"%s\", num_values=%d, lower=%p, upper=%p)", (void *)ipp, group, ippTagString(group), name, num_values, (void *)lower, (void *)upper));
    686 
    687  /*
    688   * Range check input...
    689   */
    690 
    691   if (!ipp || !name || group < IPP_TAG_ZERO ||
    692       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
    693       num_values < 1)
    694     return (NULL);
    695 
    696  /*
    697   * Create the attribute...
    698   */
    699 
    700   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RANGE, num_values)) == NULL)
    701     return (NULL);
    702 
    703   if (lower && upper)
    704   {
    705     for (i = num_values, value = attr->values;
    706 	 i > 0;
    707 	 i --, value ++)
    708     {
    709       value->range.lower = *lower++;
    710       value->range.upper = *upper++;
    711     }
    712   }
    713 
    714   return (attr);
    715 }
    716 
    717 
    718 /*
    719  * 'ippAddResolution()' - Add a resolution value to an IPP message.
    720  *
    721  * The @code ipp@ parameter refers to an IPP message previously created using
    722  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    723  *
    724  * The @code group@ parameter specifies the IPP attribute group tag: none
    725  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
    726  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
    727  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
    728  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    729  */
    730 
    731 ipp_attribute_t *			/* O - New attribute */
    732 ippAddResolution(ipp_t      *ipp,	/* I - IPP message */
    733         	 ipp_tag_t  group,	/* I - IPP group */
    734 		 const char *name,	/* I - Name of attribute */
    735 		 ipp_res_t  units,	/* I - Units for resolution */
    736 		 int        xres,	/* I - X resolution */
    737 		 int        yres)	/* I - Y resolution */
    738 {
    739   ipp_attribute_t	*attr;		/* New attribute */
    740 
    741 
    742   DEBUG_printf(("ippAddResolution(ipp=%p, group=%02x(%s), name=\"%s\", units=%d, xres=%d, yres=%d)", (void *)ipp, group,
    743 		ippTagString(group), name, units, xres, yres));
    744 
    745  /*
    746   * Range check input...
    747   */
    748 
    749   if (!ipp || !name || group < IPP_TAG_ZERO ||
    750       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
    751       units < IPP_RES_PER_INCH || units > IPP_RES_PER_CM ||
    752       xres < 0 || yres < 0)
    753     return (NULL);
    754 
    755  /*
    756   * Create the attribute...
    757   */
    758 
    759   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RESOLUTION, 1)) == NULL)
    760     return (NULL);
    761 
    762   attr->values[0].resolution.xres  = xres;
    763   attr->values[0].resolution.yres  = yres;
    764   attr->values[0].resolution.units = units;
    765 
    766   return (attr);
    767 }
    768 
    769 
    770 /*
    771  * 'ippAddResolutions()' - Add resolution values to an IPP message.
    772  *
    773  * The @code ipp@ parameter refers to an IPP message previously created using
    774  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    775  *
    776  * The @code group@ parameter specifies the IPP attribute group tag: none
    777  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
    778  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
    779  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
    780  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    781  */
    782 
    783 ipp_attribute_t *			/* O - New attribute */
    784 ippAddResolutions(ipp_t      *ipp,	/* I - IPP message */
    785         	  ipp_tag_t  group,	/* I - IPP group */
    786 		  const char *name,	/* I - Name of attribute */
    787 		  int        num_values,/* I - Number of values */
    788 		  ipp_res_t  units,	/* I - Units for resolution */
    789 		  const int  *xres,	/* I - X resolutions */
    790 		  const int  *yres)	/* I - Y resolutions */
    791 {
    792   int			i;		/* Looping var */
    793   ipp_attribute_t	*attr;		/* New attribute */
    794   _ipp_value_t		*value;		/* Current value */
    795 
    796 
    797   DEBUG_printf(("ippAddResolutions(ipp=%p, group=%02x(%s), name=\"%s\", num_value=%d, units=%d, xres=%p, yres=%p)", (void *)ipp, group, ippTagString(group), name, num_values, units, (void *)xres, (void *)yres));
    798 
    799  /*
    800   * Range check input...
    801   */
    802 
    803   if (!ipp || !name || group < IPP_TAG_ZERO ||
    804       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
    805       num_values < 1 ||
    806       units < IPP_RES_PER_INCH || units > IPP_RES_PER_CM)
    807     return (NULL);
    808 
    809  /*
    810   * Create the attribute...
    811   */
    812 
    813   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RESOLUTION, num_values)) == NULL)
    814     return (NULL);
    815 
    816   if (xres && yres)
    817   {
    818     for (i = num_values, value = attr->values;
    819 	 i > 0;
    820 	 i --, value ++)
    821     {
    822       value->resolution.xres  = *xres++;
    823       value->resolution.yres  = *yres++;
    824       value->resolution.units = units;
    825     }
    826   }
    827 
    828   return (attr);
    829 }
    830 
    831 
    832 /*
    833  * 'ippAddSeparator()' - Add a group separator to an IPP message.
    834  *
    835  * The @code ipp@ parameter refers to an IPP message previously created using
    836  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    837  */
    838 
    839 ipp_attribute_t *			/* O - New attribute */
    840 ippAddSeparator(ipp_t *ipp)		/* I - IPP message */
    841 {
    842   DEBUG_printf(("ippAddSeparator(ipp=%p)", (void *)ipp));
    843 
    844  /*
    845   * Range check input...
    846   */
    847 
    848   if (!ipp)
    849     return (NULL);
    850 
    851  /*
    852   * Create the attribute...
    853   */
    854 
    855   return (ipp_add_attr(ipp, NULL, IPP_TAG_ZERO, IPP_TAG_ZERO, 0));
    856 }
    857 
    858 
    859 /*
    860  * 'ippAddString()' - Add a language-encoded string to an IPP message.
    861  *
    862  * The @code ipp@ parameter refers to an IPP message previously created using
    863  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    864  *
    865  * The @code group@ parameter specifies the IPP attribute group tag: none
    866  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
    867  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
    868  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
    869  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    870  *
    871  * Supported string values include charset (@code IPP_TAG_CHARSET@), keyword
    872  * (@code IPP_TAG_KEYWORD@), language (@code IPP_TAG_LANGUAGE@), mimeMediaType
    873  * (@code IPP_TAG_MIMETYPE@), name (@code IPP_TAG_NAME@), nameWithLanguage
    874  * (@code IPP_TAG_NAMELANG), text (@code IPP_TAG_TEXT@), textWithLanguage
    875  * (@code IPP_TAG_TEXTLANG@), uri (@code IPP_TAG_URI@), and uriScheme
    876  * (@code IPP_TAG_URISCHEME@).
    877  *
    878  * The @code language@ parameter must be non-@code NULL@ for nameWithLanguage and
    879  * textWithLanguage string values and must be @code NULL@ for all other string values.
    880  */
    881 
    882 ipp_attribute_t *			/* O - New attribute */
    883 ippAddString(ipp_t      *ipp,		/* I - IPP message */
    884              ipp_tag_t  group,		/* I - IPP group */
    885 	     ipp_tag_t  value_tag,	/* I - Type of attribute */
    886              const char *name,		/* I - Name of attribute */
    887              const char *language,	/* I - Language code */
    888              const char *value)		/* I - Value */
    889 {
    890   ipp_tag_t		temp_tag;	/* Temporary value tag (masked) */
    891   ipp_attribute_t	*attr;		/* New attribute */
    892   char			code[IPP_MAX_LANGUAGE];
    893 					/* Charset/language code buffer */
    894 
    895 
    896   DEBUG_printf(("ippAddString(ipp=%p, group=%02x(%s), value_tag=%02x(%s), name=\"%s\", language=\"%s\", value=\"%s\")", (void *)ipp, group, ippTagString(group), value_tag, ippTagString(value_tag), name, language, value));
    897 
    898  /*
    899   * Range check input...
    900   */
    901 
    902   temp_tag = (ipp_tag_t)((int)value_tag & IPP_TAG_CUPS_MASK);
    903 
    904 #if 0
    905   if (!ipp || !name || group < IPP_TAG_ZERO ||
    906       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
    907       (temp_tag < IPP_TAG_TEXT && temp_tag != IPP_TAG_TEXTLANG &&
    908        temp_tag != IPP_TAG_NAMELANG) || temp_tag > IPP_TAG_MIMETYPE)
    909     return (NULL);
    910 
    911   if ((temp_tag == IPP_TAG_TEXTLANG || temp_tag == IPP_TAG_NAMELANG)
    912           != (language != NULL))
    913     return (NULL);
    914 #else
    915   if (!ipp || !name || group < IPP_TAG_ZERO ||
    916       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
    917     return (NULL);
    918 #endif /* 0 */
    919 
    920  /*
    921   * See if we need to map charset, language, or locale values...
    922   */
    923 
    924   if (language && ((int)value_tag & IPP_TAG_CUPS_CONST) &&
    925       strcmp(language, ipp_lang_code(language, code, sizeof(code))))
    926     value_tag = temp_tag;		/* Don't do a fast copy */
    927   else if (value && value_tag == (ipp_tag_t)(IPP_TAG_CHARSET | IPP_TAG_CUPS_CONST) &&
    928            strcmp(value, ipp_get_code(value, code, sizeof(code))))
    929     value_tag = temp_tag;		/* Don't do a fast copy */
    930   else if (value && value_tag == (ipp_tag_t)(IPP_TAG_LANGUAGE | IPP_TAG_CUPS_CONST) &&
    931            strcmp(value, ipp_lang_code(value, code, sizeof(code))))
    932     value_tag = temp_tag;		/* Don't do a fast copy */
    933 
    934  /*
    935   * Create the attribute...
    936   */
    937 
    938   if ((attr = ipp_add_attr(ipp, name, group, value_tag, 1)) == NULL)
    939     return (NULL);
    940 
    941  /*
    942   * Initialize the attribute data...
    943   */
    944 
    945   if ((int)value_tag & IPP_TAG_CUPS_CONST)
    946   {
    947     attr->values[0].string.language = (char *)language;
    948     attr->values[0].string.text     = (char *)value;
    949   }
    950   else
    951   {
    952     if (language)
    953       attr->values[0].string.language = _cupsStrAlloc(ipp_lang_code(language, code,
    954 						      sizeof(code)));
    955 
    956     if (value)
    957     {
    958       if (value_tag == IPP_TAG_CHARSET)
    959 	attr->values[0].string.text = _cupsStrAlloc(ipp_get_code(value, code,
    960 								 sizeof(code)));
    961       else if (value_tag == IPP_TAG_LANGUAGE)
    962 	attr->values[0].string.text = _cupsStrAlloc(ipp_lang_code(value, code,
    963 								  sizeof(code)));
    964       else
    965 	attr->values[0].string.text = _cupsStrAlloc(value);
    966     }
    967   }
    968 
    969   return (attr);
    970 }
    971 
    972 
    973 /*
    974  * 'ippAddStringf()' - Add a formatted string to an IPP message.
    975  *
    976  * The @code ipp@ parameter refers to an IPP message previously created using
    977  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
    978  *
    979  * The @code group@ parameter specifies the IPP attribute group tag: none
    980  * (@code IPP_TAG_ZERO@, for member attributes), document
    981  * (@code IPP_TAG_DOCUMENT@), event notification
    982  * (@code IPP_TAG_EVENT_NOTIFICATION@), operation (@code IPP_TAG_OPERATION@),
    983  * printer (@code IPP_TAG_PRINTER@), subscription (@code IPP_TAG_SUBSCRIPTION@),
    984  * or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
    985  *
    986  * Supported string values include charset (@code IPP_TAG_CHARSET@), keyword
    987  * (@code IPP_TAG_KEYWORD@), language (@code IPP_TAG_LANGUAGE@), mimeMediaType
    988  * (@code IPP_TAG_MIMETYPE@), name (@code IPP_TAG_NAME@), nameWithLanguage
    989  * (@code IPP_TAG_NAMELANG), text (@code IPP_TAG_TEXT@), textWithLanguage
    990  * (@code IPP_TAG_TEXTLANG@), uri (@code IPP_TAG_URI@), and uriScheme
    991  * (@code IPP_TAG_URISCHEME@).
    992  *
    993  * The @code language@ parameter must be non-@code NULL@ for nameWithLanguage
    994  * and textWithLanguage string values and must be @code NULL@ for all other
    995  * string values.
    996  *
    997  * The @code format@ parameter uses formatting characters compatible with the
    998  * printf family of standard functions.  Additional arguments follow it as
    999  * needed.  The formatted string is truncated as needed to the maximum length of
   1000  * the corresponding value type.
   1001  *
   1002  * @since CUPS 1.7/macOS 10.9@
   1003  */
   1004 
   1005 ipp_attribute_t *			/* O - New attribute */
   1006 ippAddStringf(ipp_t      *ipp,		/* I - IPP message */
   1007               ipp_tag_t  group,		/* I - IPP group */
   1008 	      ipp_tag_t  value_tag,	/* I - Type of attribute */
   1009 	      const char *name,		/* I - Name of attribute */
   1010 	      const char *language,	/* I - Language code (@code NULL@ for default) */
   1011 	      const char *format,	/* I - Printf-style format string */
   1012 	      ...)			/* I - Additional arguments as needed */
   1013 {
   1014   ipp_attribute_t	*attr;		/* New attribute */
   1015   va_list		ap;		/* Argument pointer */
   1016 
   1017 
   1018   va_start(ap, format);
   1019   attr = ippAddStringfv(ipp, group, value_tag, name, language, format, ap);
   1020   va_end(ap);
   1021 
   1022   return (attr);
   1023 }
   1024 
   1025 
   1026 /*
   1027  * 'ippAddStringfv()' - Add a formatted string to an IPP message.
   1028  *
   1029  * The @code ipp@ parameter refers to an IPP message previously created using
   1030  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   1031  *
   1032  * The @code group@ parameter specifies the IPP attribute group tag: none
   1033  * (@code IPP_TAG_ZERO@, for member attributes), document
   1034  * (@code IPP_TAG_DOCUMENT@), event notification
   1035  * (@code IPP_TAG_EVENT_NOTIFICATION@), operation (@code IPP_TAG_OPERATION@),
   1036  * printer (@code IPP_TAG_PRINTER@), subscription (@code IPP_TAG_SUBSCRIPTION@),
   1037  * or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
   1038  *
   1039  * Supported string values include charset (@code IPP_TAG_CHARSET@), keyword
   1040  * (@code IPP_TAG_KEYWORD@), language (@code IPP_TAG_LANGUAGE@), mimeMediaType
   1041  * (@code IPP_TAG_MIMETYPE@), name (@code IPP_TAG_NAME@), nameWithLanguage
   1042  * (@code IPP_TAG_NAMELANG), text (@code IPP_TAG_TEXT@), textWithLanguage
   1043  * (@code IPP_TAG_TEXTLANG@), uri (@code IPP_TAG_URI@), and uriScheme
   1044  * (@code IPP_TAG_URISCHEME@).
   1045  *
   1046  * The @code language@ parameter must be non-@code NULL@ for nameWithLanguage
   1047  * and textWithLanguage string values and must be @code NULL@ for all other
   1048  * string values.
   1049  *
   1050  * The @code format@ parameter uses formatting characters compatible with the
   1051  * printf family of standard functions.  Additional arguments are passed in the
   1052  * stdarg pointer @code ap@.  The formatted string is truncated as needed to the
   1053  * maximum length of the corresponding value type.
   1054  *
   1055  * @since CUPS 1.7/macOS 10.9@
   1056  */
   1057 
   1058 ipp_attribute_t *			/* O - New attribute */
   1059 ippAddStringfv(ipp_t      *ipp,		/* I - IPP message */
   1060                ipp_tag_t  group,	/* I - IPP group */
   1061 	       ipp_tag_t  value_tag,	/* I - Type of attribute */
   1062 	       const char *name,	/* I - Name of attribute */
   1063 	       const char *language,	/* I - Language code (@code NULL@ for default) */
   1064 	       const char *format,	/* I - Printf-style format string */
   1065 	       va_list    ap)		/* I - Additional arguments */
   1066 {
   1067   char		buffer[IPP_MAX_TEXT + 4];
   1068 					/* Formatted text string */
   1069   ssize_t	bytes,			/* Length of formatted value */
   1070 		max_bytes;		/* Maximum number of bytes for value */
   1071 
   1072 
   1073  /*
   1074   * Range check input...
   1075   */
   1076 
   1077   if (!ipp || !name || group < IPP_TAG_ZERO ||
   1078       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
   1079       (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG &&
   1080        value_tag != IPP_TAG_NAMELANG) || value_tag > IPP_TAG_MIMETYPE ||
   1081       !format)
   1082     return (NULL);
   1083 
   1084   if ((value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_NAMELANG)
   1085           != (language != NULL))
   1086     return (NULL);
   1087 
   1088  /*
   1089   * Format the string...
   1090   */
   1091 
   1092   if (!strcmp(format, "%s"))
   1093   {
   1094    /*
   1095     * Optimize the simple case...
   1096     */
   1097 
   1098     const char *s = va_arg(ap, char *);
   1099 
   1100     if (!s)
   1101       s = "(null)";
   1102 
   1103     bytes = (ssize_t)strlen(s);
   1104     strlcpy(buffer, s, sizeof(buffer));
   1105   }
   1106   else
   1107   {
   1108    /*
   1109     * Do a full formatting of the message...
   1110     */
   1111 
   1112     if ((bytes = vsnprintf(buffer, sizeof(buffer), format, ap)) < 0)
   1113       return (NULL);
   1114   }
   1115 
   1116  /*
   1117   * Limit the length of the string...
   1118   */
   1119 
   1120   switch (value_tag)
   1121   {
   1122     default :
   1123     case IPP_TAG_TEXT :
   1124     case IPP_TAG_TEXTLANG :
   1125         max_bytes = IPP_MAX_TEXT;
   1126         break;
   1127 
   1128     case IPP_TAG_NAME :
   1129     case IPP_TAG_NAMELANG :
   1130         max_bytes = IPP_MAX_NAME;
   1131         break;
   1132 
   1133     case IPP_TAG_CHARSET :
   1134         max_bytes = IPP_MAX_CHARSET;
   1135         break;
   1136 
   1137     case IPP_TAG_KEYWORD :
   1138         max_bytes = IPP_MAX_KEYWORD;
   1139         break;
   1140 
   1141     case IPP_TAG_LANGUAGE :
   1142         max_bytes = IPP_MAX_LANGUAGE;
   1143         break;
   1144 
   1145     case IPP_TAG_MIMETYPE :
   1146         max_bytes = IPP_MAX_MIMETYPE;
   1147         break;
   1148 
   1149     case IPP_TAG_URI :
   1150         max_bytes = IPP_MAX_URI;
   1151         break;
   1152 
   1153     case IPP_TAG_URISCHEME :
   1154         max_bytes = IPP_MAX_URISCHEME;
   1155         break;
   1156   }
   1157 
   1158   if (bytes >= max_bytes)
   1159   {
   1160     char	*bufmax,		/* Buffer at max_bytes */
   1161 		*bufptr;		/* Pointer into buffer */
   1162 
   1163     bufptr = buffer + strlen(buffer) - 1;
   1164     bufmax = buffer + max_bytes - 1;
   1165 
   1166     while (bufptr > bufmax)
   1167     {
   1168       if (*bufptr & 0x80)
   1169       {
   1170         while ((*bufptr & 0xc0) == 0x80 && bufptr > buffer)
   1171           bufptr --;
   1172       }
   1173 
   1174       bufptr --;
   1175     }
   1176 
   1177     *bufptr = '\0';
   1178   }
   1179 
   1180  /*
   1181   * Add the formatted string and return...
   1182   */
   1183 
   1184   return (ippAddString(ipp, group, value_tag, name, language, buffer));
   1185 }
   1186 
   1187 
   1188 /*
   1189  * 'ippAddStrings()' - Add language-encoded strings to an IPP message.
   1190  *
   1191  * The @code ipp@ parameter refers to an IPP message previously created using
   1192  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   1193  *
   1194  * The @code group@ parameter specifies the IPP attribute group tag: none
   1195  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
   1196  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
   1197  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
   1198  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
   1199  *
   1200  * Supported string values include charset (@code IPP_TAG_CHARSET@), keyword
   1201  * (@code IPP_TAG_KEYWORD@), language (@code IPP_TAG_LANGUAGE@), mimeMediaType
   1202  * (@code IPP_TAG_MIMETYPE@), name (@code IPP_TAG_NAME@), nameWithLanguage
   1203  * (@code IPP_TAG_NAMELANG), text (@code IPP_TAG_TEXT@), textWithLanguage
   1204  * (@code IPP_TAG_TEXTLANG@), uri (@code IPP_TAG_URI@), and uriScheme
   1205  * (@code IPP_TAG_URISCHEME@).
   1206  *
   1207  * The @code language@ parameter must be non-@code NULL@ for nameWithLanguage and
   1208  * textWithLanguage string values and must be @code NULL@ for all other string values.
   1209  */
   1210 
   1211 ipp_attribute_t *			/* O - New attribute */
   1212 ippAddStrings(
   1213     ipp_t              *ipp,		/* I - IPP message */
   1214     ipp_tag_t          group,		/* I - IPP group */
   1215     ipp_tag_t          value_tag,	/* I - Type of attribute */
   1216     const char         *name,		/* I - Name of attribute */
   1217     int                num_values,	/* I - Number of values */
   1218     const char         *language,	/* I - Language code (@code NULL@ for default) */
   1219     const char * const *values)		/* I - Values */
   1220 {
   1221   int			i;		/* Looping var */
   1222   ipp_tag_t		temp_tag;	/* Temporary value tag (masked) */
   1223   ipp_attribute_t	*attr;		/* New attribute */
   1224   _ipp_value_t		*value;		/* Current value */
   1225   char			code[32];	/* Language/charset value buffer */
   1226 
   1227 
   1228   DEBUG_printf(("ippAddStrings(ipp=%p, group=%02x(%s), value_tag=%02x(%s), name=\"%s\", num_values=%d, language=\"%s\", values=%p)", (void *)ipp, group, ippTagString(group), value_tag, ippTagString(value_tag), name, num_values, language, (void *)values));
   1229 
   1230  /*
   1231   * Range check input...
   1232   */
   1233 
   1234   temp_tag = (ipp_tag_t)((int)value_tag & IPP_TAG_CUPS_MASK);
   1235 
   1236 #if 0
   1237   if (!ipp || !name || group < IPP_TAG_ZERO ||
   1238       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
   1239       (temp_tag < IPP_TAG_TEXT && temp_tag != IPP_TAG_TEXTLANG &&
   1240        temp_tag != IPP_TAG_NAMELANG) || temp_tag > IPP_TAG_MIMETYPE ||
   1241       num_values < 1)
   1242     return (NULL);
   1243 
   1244   if ((temp_tag == IPP_TAG_TEXTLANG || temp_tag == IPP_TAG_NAMELANG)
   1245           != (language != NULL))
   1246     return (NULL);
   1247 #else
   1248   if (!ipp || !name || group < IPP_TAG_ZERO ||
   1249       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
   1250       num_values < 1)
   1251     return (NULL);
   1252 #endif /* 0 */
   1253 
   1254  /*
   1255   * See if we need to map charset, language, or locale values...
   1256   */
   1257 
   1258   if (language && ((int)value_tag & IPP_TAG_CUPS_CONST) &&
   1259       strcmp(language, ipp_lang_code(language, code, sizeof(code))))
   1260     value_tag = temp_tag;		/* Don't do a fast copy */
   1261   else if (values && value_tag == (ipp_tag_t)(IPP_TAG_CHARSET | IPP_TAG_CUPS_CONST))
   1262   {
   1263     for (i = 0; i < num_values; i ++)
   1264       if (strcmp(values[i], ipp_get_code(values[i], code, sizeof(code))))
   1265       {
   1266 	value_tag = temp_tag;		/* Don't do a fast copy */
   1267         break;
   1268       }
   1269   }
   1270   else if (values && value_tag == (ipp_tag_t)(IPP_TAG_LANGUAGE | IPP_TAG_CUPS_CONST))
   1271   {
   1272     for (i = 0; i < num_values; i ++)
   1273       if (strcmp(values[i], ipp_lang_code(values[i], code, sizeof(code))))
   1274       {
   1275 	value_tag = temp_tag;		/* Don't do a fast copy */
   1276         break;
   1277       }
   1278   }
   1279 
   1280  /*
   1281   * Create the attribute...
   1282   */
   1283 
   1284   if ((attr = ipp_add_attr(ipp, name, group, value_tag, num_values)) == NULL)
   1285     return (NULL);
   1286 
   1287  /*
   1288   * Initialize the attribute data...
   1289   */
   1290 
   1291   for (i = num_values, value = attr->values;
   1292        i > 0;
   1293        i --, value ++)
   1294   {
   1295     if (language)
   1296     {
   1297       if (value == attr->values)
   1298       {
   1299         if ((int)value_tag & IPP_TAG_CUPS_CONST)
   1300           value->string.language = (char *)language;
   1301         else
   1302           value->string.language = _cupsStrAlloc(ipp_lang_code(language, code,
   1303                                                                sizeof(code)));
   1304       }
   1305       else
   1306 	value->string.language = attr->values[0].string.language;
   1307     }
   1308 
   1309     if (values)
   1310     {
   1311       if ((int)value_tag & IPP_TAG_CUPS_CONST)
   1312         value->string.text = (char *)*values++;
   1313       else if (value_tag == IPP_TAG_CHARSET)
   1314 	value->string.text = _cupsStrAlloc(ipp_get_code(*values++, code, sizeof(code)));
   1315       else if (value_tag == IPP_TAG_LANGUAGE)
   1316 	value->string.text = _cupsStrAlloc(ipp_lang_code(*values++, code, sizeof(code)));
   1317       else
   1318 	value->string.text = _cupsStrAlloc(*values++);
   1319     }
   1320   }
   1321 
   1322   return (attr);
   1323 }
   1324 
   1325 
   1326 /*
   1327  * 'ippContainsInteger()' - Determine whether an attribute contains the
   1328  *                          specified value or is within the list of ranges.
   1329  *
   1330  * Returns non-zero when the attribute contains either a matching integer or
   1331  * enum value, or the value falls within one of the rangeOfInteger values for
   1332  * the attribute.
   1333  *
   1334  * @since CUPS 1.7/macOS 10.9@
   1335  */
   1336 
   1337 int					/* O - 1 on a match, 0 on no match */
   1338 ippContainsInteger(
   1339     ipp_attribute_t *attr,		/* I - Attribute */
   1340     int             value)		/* I - Integer/enum value */
   1341 {
   1342   int		i;			/* Looping var */
   1343   _ipp_value_t	*avalue;		/* Current attribute value */
   1344 
   1345 
   1346  /*
   1347   * Range check input...
   1348   */
   1349 
   1350   if (!attr)
   1351     return (0);
   1352 
   1353   if (attr->value_tag != IPP_TAG_INTEGER && attr->value_tag != IPP_TAG_ENUM &&
   1354       attr->value_tag != IPP_TAG_RANGE)
   1355     return (0);
   1356 
   1357  /*
   1358   * Compare...
   1359   */
   1360 
   1361   if (attr->value_tag == IPP_TAG_RANGE)
   1362   {
   1363     for (i = attr->num_values, avalue = attr->values; i > 0; i --, avalue ++)
   1364       if (value >= avalue->range.lower && value <= avalue->range.upper)
   1365         return (1);
   1366   }
   1367   else
   1368   {
   1369     for (i = attr->num_values, avalue = attr->values; i > 0; i --, avalue ++)
   1370       if (value == avalue->integer)
   1371         return (1);
   1372   }
   1373 
   1374   return (0);
   1375 }
   1376 
   1377 
   1378 /*
   1379  * 'ippContainsString()' - Determine whether an attribute contains the
   1380  *                         specified string value.
   1381  *
   1382  * Returns non-zero when the attribute contains a matching charset, keyword,
   1383  * naturalLanguage, mimeMediaType, name, text, uri, or uriScheme value.
   1384  *
   1385  * @since CUPS 1.7/macOS 10.9@
   1386  */
   1387 
   1388 int					/* O - 1 on a match, 0 on no match */
   1389 ippContainsString(
   1390     ipp_attribute_t *attr,		/* I - Attribute */
   1391     const char      *value)		/* I - String value */
   1392 {
   1393   int		i;			/* Looping var */
   1394   _ipp_value_t	*avalue;		/* Current attribute value */
   1395 
   1396 
   1397   DEBUG_printf(("ippContainsString(attr=%p, value=\"%s\")", (void *)attr, value));
   1398 
   1399  /*
   1400   * Range check input...
   1401   */
   1402 
   1403   if (!attr || !value)
   1404   {
   1405     DEBUG_puts("1ippContainsString: Returning 0 (bad input)");
   1406     return (0);
   1407   }
   1408 
   1409  /*
   1410   * Compare...
   1411   */
   1412 
   1413   DEBUG_printf(("1ippContainsString: attr %s, %s with %d values.",
   1414 		attr->name, ippTagString(attr->value_tag),
   1415 		attr->num_values));
   1416 
   1417   switch (attr->value_tag & IPP_TAG_CUPS_MASK)
   1418   {
   1419     case IPP_TAG_CHARSET :
   1420     case IPP_TAG_KEYWORD :
   1421     case IPP_TAG_LANGUAGE :
   1422     case IPP_TAG_URI :
   1423     case IPP_TAG_URISCHEME :
   1424 	for (i = attr->num_values, avalue = attr->values;
   1425 	     i > 0;
   1426 	     i --, avalue ++)
   1427 	{
   1428 	  DEBUG_printf(("1ippContainsString: value[%d]=\"%s\"",
   1429 	                attr->num_values - i, avalue->string.text));
   1430 
   1431 	  if (!strcmp(value, avalue->string.text))
   1432 	  {
   1433 	    DEBUG_puts("1ippContainsString: Returning 1 (match)");
   1434 	    return (1);
   1435 	  }
   1436         }
   1437 
   1438     case IPP_TAG_MIMETYPE :
   1439     case IPP_TAG_NAME :
   1440     case IPP_TAG_NAMELANG :
   1441     case IPP_TAG_TEXT :
   1442     case IPP_TAG_TEXTLANG :
   1443 	for (i = attr->num_values, avalue = attr->values;
   1444 	     i > 0;
   1445 	     i --, avalue ++)
   1446 	{
   1447 	  DEBUG_printf(("1ippContainsString: value[%d]=\"%s\"",
   1448 	                attr->num_values - i, avalue->string.text));
   1449 
   1450 	  if (!_cups_strcasecmp(value, avalue->string.text))
   1451 	  {
   1452 	    DEBUG_puts("1ippContainsString: Returning 1 (match)");
   1453 	    return (1);
   1454 	  }
   1455         }
   1456 
   1457     default :
   1458         break;
   1459   }
   1460 
   1461   DEBUG_puts("1ippContainsString: Returning 0 (no match)");
   1462 
   1463   return (0);
   1464 }
   1465 
   1466 
   1467 /*
   1468  * 'ippCopyAttribute()' - Copy an attribute.
   1469  *
   1470  * The specified attribute, @code attr@, is copied to the destination IPP message.
   1471  * When @code quickcopy@ is non-zero, a "shallow" reference copy of the attribute is
   1472  * created - this should only be done as long as the original source IPP message will
   1473  * not be freed for the life of the destination.
   1474  *
   1475  * @since CUPS 1.6/macOS 10.8@
   1476  */
   1477 
   1478 
   1479 ipp_attribute_t *			/* O - New attribute */
   1480 ippCopyAttribute(
   1481     ipp_t           *dst,		/* I - Destination IPP message */
   1482     ipp_attribute_t *srcattr,		/* I - Attribute to copy */
   1483     int             quickcopy)		/* I - 1 for a referenced copy, 0 for normal */
   1484 {
   1485   int			i;		/* Looping var */
   1486   ipp_attribute_t	*dstattr;	/* Destination attribute */
   1487   _ipp_value_t		*srcval,	/* Source value */
   1488 			*dstval;	/* Destination value */
   1489 
   1490 
   1491   DEBUG_printf(("ippCopyAttribute(dst=%p, srcattr=%p, quickcopy=%d)", (void *)dst, (void *)srcattr, quickcopy));
   1492 
   1493  /*
   1494   * Range check input...
   1495   */
   1496 
   1497   if (!dst || !srcattr)
   1498     return (NULL);
   1499 
   1500  /*
   1501   * Copy it...
   1502   */
   1503 
   1504   quickcopy = quickcopy ? IPP_TAG_CUPS_CONST : 0;
   1505 
   1506   switch (srcattr->value_tag & ~IPP_TAG_CUPS_CONST)
   1507   {
   1508     case IPP_TAG_ZERO :
   1509         dstattr = ippAddSeparator(dst);
   1510 	break;
   1511 
   1512     case IPP_TAG_UNSUPPORTED_VALUE :
   1513     case IPP_TAG_DEFAULT :
   1514     case IPP_TAG_UNKNOWN :
   1515     case IPP_TAG_NOVALUE :
   1516     case IPP_TAG_NOTSETTABLE :
   1517     case IPP_TAG_DELETEATTR :
   1518     case IPP_TAG_ADMINDEFINE :
   1519         dstattr = ippAddOutOfBand(dst, srcattr->group_tag, srcattr->value_tag & ~IPP_TAG_CUPS_CONST, srcattr->name);
   1520         break;
   1521 
   1522     case IPP_TAG_INTEGER :
   1523     case IPP_TAG_ENUM :
   1524         dstattr = ippAddIntegers(dst, srcattr->group_tag, srcattr->value_tag,
   1525 	                         srcattr->name, srcattr->num_values, NULL);
   1526         if (!dstattr)
   1527           break;
   1528 
   1529         for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
   1530              i > 0;
   1531              i --, srcval ++, dstval ++)
   1532 	  dstval->integer = srcval->integer;
   1533         break;
   1534 
   1535     case IPP_TAG_BOOLEAN :
   1536         dstattr = ippAddBooleans(dst, srcattr->group_tag, srcattr->name,
   1537 	                        srcattr->num_values, NULL);
   1538         if (!dstattr)
   1539           break;
   1540 
   1541         for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
   1542              i > 0;
   1543              i --, srcval ++, dstval ++)
   1544 	  dstval->boolean = srcval->boolean;
   1545         break;
   1546 
   1547     case IPP_TAG_TEXT :
   1548     case IPP_TAG_NAME :
   1549     case IPP_TAG_KEYWORD :
   1550     case IPP_TAG_URI :
   1551     case IPP_TAG_URISCHEME :
   1552     case IPP_TAG_CHARSET :
   1553     case IPP_TAG_LANGUAGE :
   1554     case IPP_TAG_MIMETYPE :
   1555         dstattr = ippAddStrings(dst, srcattr->group_tag,
   1556 	                        (ipp_tag_t)(srcattr->value_tag | quickcopy),
   1557 	                        srcattr->name, srcattr->num_values, NULL, NULL);
   1558         if (!dstattr)
   1559           break;
   1560 
   1561         if (quickcopy)
   1562 	{
   1563 	  for (i = srcattr->num_values, srcval = srcattr->values,
   1564 	           dstval = dstattr->values;
   1565 	       i > 0;
   1566 	       i --, srcval ++, dstval ++)
   1567 	    dstval->string.text = srcval->string.text;
   1568         }
   1569 	else if (srcattr->value_tag & IPP_TAG_CUPS_CONST)
   1570 	{
   1571 	  for (i = srcattr->num_values, srcval = srcattr->values,
   1572 	           dstval = dstattr->values;
   1573 	       i > 0;
   1574 	       i --, srcval ++, dstval ++)
   1575 	    dstval->string.text = _cupsStrAlloc(srcval->string.text);
   1576 	}
   1577 	else
   1578 	{
   1579 	  for (i = srcattr->num_values, srcval = srcattr->values,
   1580 	           dstval = dstattr->values;
   1581 	       i > 0;
   1582 	       i --, srcval ++, dstval ++)
   1583 	    dstval->string.text = _cupsStrRetain(srcval->string.text);
   1584 	}
   1585         break;
   1586 
   1587     case IPP_TAG_DATE :
   1588         if (srcattr->num_values != 1)
   1589           return (NULL);
   1590 
   1591         dstattr = ippAddDate(dst, srcattr->group_tag, srcattr->name,
   1592 	                     srcattr->values[0].date);
   1593         break;
   1594 
   1595     case IPP_TAG_RESOLUTION :
   1596         dstattr = ippAddResolutions(dst, srcattr->group_tag, srcattr->name,
   1597 	                            srcattr->num_values, IPP_RES_PER_INCH,
   1598 				    NULL, NULL);
   1599         if (!dstattr)
   1600           break;
   1601 
   1602         for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
   1603              i > 0;
   1604              i --, srcval ++, dstval ++)
   1605 	{
   1606 	  dstval->resolution.xres  = srcval->resolution.xres;
   1607 	  dstval->resolution.yres  = srcval->resolution.yres;
   1608 	  dstval->resolution.units = srcval->resolution.units;
   1609 	}
   1610         break;
   1611 
   1612     case IPP_TAG_RANGE :
   1613         dstattr = ippAddRanges(dst, srcattr->group_tag, srcattr->name,
   1614 	                       srcattr->num_values, NULL, NULL);
   1615         if (!dstattr)
   1616           break;
   1617 
   1618         for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
   1619              i > 0;
   1620              i --, srcval ++, dstval ++)
   1621 	{
   1622 	  dstval->range.lower = srcval->range.lower;
   1623 	  dstval->range.upper = srcval->range.upper;
   1624 	}
   1625         break;
   1626 
   1627     case IPP_TAG_TEXTLANG :
   1628     case IPP_TAG_NAMELANG :
   1629         dstattr = ippAddStrings(dst, srcattr->group_tag,
   1630 	                        (ipp_tag_t)(srcattr->value_tag | quickcopy),
   1631 	                        srcattr->name, srcattr->num_values, NULL, NULL);
   1632         if (!dstattr)
   1633           break;
   1634 
   1635         if (quickcopy)
   1636 	{
   1637 	  for (i = srcattr->num_values, srcval = srcattr->values,
   1638 	           dstval = dstattr->values;
   1639 	       i > 0;
   1640 	       i --, srcval ++, dstval ++)
   1641 	  {
   1642             dstval->string.language = srcval->string.language;
   1643 	    dstval->string.text     = srcval->string.text;
   1644           }
   1645         }
   1646 	else if (srcattr->value_tag & IPP_TAG_CUPS_CONST)
   1647 	{
   1648 	  for (i = srcattr->num_values, srcval = srcattr->values,
   1649 	           dstval = dstattr->values;
   1650 	       i > 0;
   1651 	       i --, srcval ++, dstval ++)
   1652 	  {
   1653 	    if (srcval == srcattr->values)
   1654               dstval->string.language = _cupsStrAlloc(srcval->string.language);
   1655 	    else
   1656               dstval->string.language = dstattr->values[0].string.language;
   1657 
   1658 	    dstval->string.text = _cupsStrAlloc(srcval->string.text);
   1659           }
   1660         }
   1661 	else
   1662 	{
   1663 	  for (i = srcattr->num_values, srcval = srcattr->values,
   1664 	           dstval = dstattr->values;
   1665 	       i > 0;
   1666 	       i --, srcval ++, dstval ++)
   1667 	  {
   1668 	    if (srcval == srcattr->values)
   1669               dstval->string.language = _cupsStrRetain(srcval->string.language);
   1670 	    else
   1671               dstval->string.language = dstattr->values[0].string.language;
   1672 
   1673 	    dstval->string.text = _cupsStrRetain(srcval->string.text);
   1674           }
   1675         }
   1676         break;
   1677 
   1678     case IPP_TAG_BEGIN_COLLECTION :
   1679         dstattr = ippAddCollections(dst, srcattr->group_tag, srcattr->name,
   1680 	                            srcattr->num_values, NULL);
   1681         if (!dstattr)
   1682           break;
   1683 
   1684         for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
   1685              i > 0;
   1686              i --, srcval ++, dstval ++)
   1687 	{
   1688 	  dstval->collection = srcval->collection;
   1689 	  srcval->collection->use ++;
   1690 	}
   1691         break;
   1692 
   1693     case IPP_TAG_STRING :
   1694     default :
   1695         /* TODO: Implement quick copy for unknown/octetString values */
   1696         dstattr = ippAddIntegers(dst, srcattr->group_tag, srcattr->value_tag,
   1697 	                         srcattr->name, srcattr->num_values, NULL);
   1698         if (!dstattr)
   1699           break;
   1700 
   1701         for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
   1702              i > 0;
   1703              i --, srcval ++, dstval ++)
   1704 	{
   1705 	  dstval->unknown.length = srcval->unknown.length;
   1706 
   1707 	  if (dstval->unknown.length > 0)
   1708 	  {
   1709 	    if ((dstval->unknown.data = malloc((size_t)dstval->unknown.length)) == NULL)
   1710 	      dstval->unknown.length = 0;
   1711 	    else
   1712 	      memcpy(dstval->unknown.data, srcval->unknown.data, (size_t)dstval->unknown.length);
   1713 	  }
   1714 	}
   1715         break; /* anti-compiler-warning-code */
   1716   }
   1717 
   1718   return (dstattr);
   1719 }
   1720 
   1721 
   1722 /*
   1723  * 'ippCopyAttributes()' - Copy attributes from one IPP message to another.
   1724  *
   1725  * Zero or more attributes are copied from the source IPP message, @code src@, to the
   1726  * destination IPP message, @code dst@. When @code quickcopy@ is non-zero, a "shallow"
   1727  * reference copy of the attribute is created - this should only be done as long as the
   1728  * original source IPP message will not be freed for the life of the destination.
   1729  *
   1730  * The @code cb@ and @code context@ parameters provide a generic way to "filter" the
   1731  * attributes that are copied - the function must return 1 to copy the attribute or
   1732  * 0 to skip it. The function may also choose to do a partial copy of the source attribute
   1733  * itself.
   1734  *
   1735  * @since CUPS 1.6/macOS 10.8@
   1736  */
   1737 
   1738 int					/* O - 1 on success, 0 on error */
   1739 ippCopyAttributes(
   1740     ipp_t        *dst,			/* I - Destination IPP message */
   1741     ipp_t        *src,			/* I - Source IPP message */
   1742     int          quickcopy,		/* I - 1 for a referenced copy, 0 for normal */
   1743     ipp_copycb_t cb,			/* I - Copy callback or @code NULL@ for none */
   1744     void         *context)		/* I - Context pointer */
   1745 {
   1746   ipp_attribute_t	*srcattr;	/* Source attribute */
   1747 
   1748 
   1749   DEBUG_printf(("ippCopyAttributes(dst=%p, src=%p, quickcopy=%d, cb=%p, context=%p)", (void *)dst, (void *)src, quickcopy, (void *)cb, context));
   1750 
   1751  /*
   1752   * Range check input...
   1753   */
   1754 
   1755   if (!dst || !src)
   1756     return (0);
   1757 
   1758  /*
   1759   * Loop through source attributes and copy as needed...
   1760   */
   1761 
   1762   for (srcattr = src->attrs; srcattr; srcattr = srcattr->next)
   1763     if (!cb || (*cb)(context, dst, srcattr))
   1764       if (!ippCopyAttribute(dst, srcattr, quickcopy))
   1765         return (0);
   1766 
   1767   return (1);
   1768 }
   1769 
   1770 
   1771 /*
   1772  * 'ippDateToTime()' - Convert from RFC 2579 Date/Time format to time in
   1773  *                     seconds.
   1774  */
   1775 
   1776 time_t					/* O - UNIX time value */
   1777 ippDateToTime(const ipp_uchar_t *date)	/* I - RFC 2579 date info */
   1778 {
   1779   struct tm	unixdate;		/* UNIX date/time info */
   1780   time_t	t;			/* Computed time */
   1781 
   1782 
   1783   if (!date)
   1784     return (0);
   1785 
   1786   memset(&unixdate, 0, sizeof(unixdate));
   1787 
   1788  /*
   1789   * RFC-2579 date/time format is:
   1790   *
   1791   *    Byte(s)  Description
   1792   *    -------  -----------
   1793   *    0-1      Year (0 to 65535)
   1794   *    2        Month (1 to 12)
   1795   *    3        Day (1 to 31)
   1796   *    4        Hours (0 to 23)
   1797   *    5        Minutes (0 to 59)
   1798   *    6        Seconds (0 to 60, 60 = "leap second")
   1799   *    7        Deciseconds (0 to 9)
   1800   *    8        +/- UTC
   1801   *    9        UTC hours (0 to 11)
   1802   *    10       UTC minutes (0 to 59)
   1803   */
   1804 
   1805   unixdate.tm_year = ((date[0] << 8) | date[1]) - 1900;
   1806   unixdate.tm_mon  = date[2] - 1;
   1807   unixdate.tm_mday = date[3];
   1808   unixdate.tm_hour = date[4];
   1809   unixdate.tm_min  = date[5];
   1810   unixdate.tm_sec  = date[6];
   1811 
   1812   t = mktime(&unixdate);
   1813 
   1814   if (date[8] == '-')
   1815     t += date[9] * 3600 + date[10] * 60;
   1816   else
   1817     t -= date[9] * 3600 + date[10] * 60;
   1818 
   1819   return (t);
   1820 }
   1821 
   1822 
   1823 /*
   1824  * 'ippDelete()' - Delete an IPP message.
   1825  */
   1826 
   1827 void
   1828 ippDelete(ipp_t *ipp)			/* I - IPP message */
   1829 {
   1830   ipp_attribute_t	*attr,		/* Current attribute */
   1831 			*next;		/* Next attribute */
   1832 
   1833 
   1834   DEBUG_printf(("ippDelete(ipp=%p)", (void *)ipp));
   1835 
   1836   if (!ipp)
   1837     return;
   1838 
   1839   ipp->use --;
   1840   if (ipp->use > 0)
   1841   {
   1842     DEBUG_printf(("4debug_retain: %p IPP message (use=%d)", (void *)ipp, ipp->use));
   1843     return;
   1844   }
   1845 
   1846   DEBUG_printf(("4debug_free: %p IPP message", (void *)ipp));
   1847 
   1848   for (attr = ipp->attrs; attr != NULL; attr = next)
   1849   {
   1850     next = attr->next;
   1851 
   1852     DEBUG_printf(("4debug_free: %p %s %s%s (%d values)", (void *)attr, attr->name, attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag), attr->num_values));
   1853 
   1854     ipp_free_values(attr, 0, attr->num_values);
   1855 
   1856     if (attr->name)
   1857       _cupsStrFree(attr->name);
   1858 
   1859     free(attr);
   1860   }
   1861 
   1862   free(ipp);
   1863 }
   1864 
   1865 
   1866 /*
   1867  * 'ippDeleteAttribute()' - Delete a single attribute in an IPP message.
   1868  *
   1869  * @since CUPS 1.1.19/macOS 10.3@
   1870  */
   1871 
   1872 void
   1873 ippDeleteAttribute(
   1874     ipp_t           *ipp,		/* I - IPP message */
   1875     ipp_attribute_t *attr)		/* I - Attribute to delete */
   1876 {
   1877   ipp_attribute_t	*current,	/* Current attribute */
   1878 			*prev;		/* Previous attribute */
   1879 
   1880 
   1881   DEBUG_printf(("ippDeleteAttribute(ipp=%p, attr=%p(%s))", (void *)ipp, (void *)attr, attr ? attr->name : "(null)"));
   1882 
   1883  /*
   1884   * Range check input...
   1885   */
   1886 
   1887   if (!attr)
   1888     return;
   1889 
   1890   DEBUG_printf(("4debug_free: %p %s %s%s (%d values)", (void *)attr, attr->name, attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag), attr->num_values));
   1891 
   1892  /*
   1893   * Find the attribute in the list...
   1894   */
   1895 
   1896   if (ipp)
   1897   {
   1898     for (current = ipp->attrs, prev = NULL;
   1899 	 current;
   1900 	 prev = current, current = current->next)
   1901       if (current == attr)
   1902       {
   1903        /*
   1904 	* Found it, remove the attribute from the list...
   1905 	*/
   1906 
   1907 	if (prev)
   1908 	  prev->next = current->next;
   1909 	else
   1910 	  ipp->attrs = current->next;
   1911 
   1912 	if (current == ipp->last)
   1913 	  ipp->last = prev;
   1914 
   1915         break;
   1916       }
   1917 
   1918     if (!current)
   1919       return;
   1920   }
   1921 
   1922  /*
   1923   * Free memory used by the attribute...
   1924   */
   1925 
   1926   ipp_free_values(attr, 0, attr->num_values);
   1927 
   1928   if (attr->name)
   1929     _cupsStrFree(attr->name);
   1930 
   1931   free(attr);
   1932 }
   1933 
   1934 
   1935 /*
   1936  * 'ippDeleteValues()' - Delete values in an attribute.
   1937  *
   1938  * The @code element@ parameter specifies the first value to delete, starting at
   1939  * 0. It must be less than the number of values returned by @link ippGetCount@.
   1940  *
   1941  * The @code attr@ parameter may be modified as a result of setting the value.
   1942  *
   1943  * Deleting all values in an attribute deletes the attribute.
   1944  *
   1945  * @since CUPS 1.6/macOS 10.8@
   1946  */
   1947 
   1948 int					/* O  - 1 on success, 0 on failure */
   1949 ippDeleteValues(
   1950     ipp_t           *ipp,		/* I  - IPP message */
   1951     ipp_attribute_t **attr,		/* IO - Attribute */
   1952     int             element,		/* I  - Index of first value to delete (0-based) */
   1953     int             count)		/* I  - Number of values to delete */
   1954 {
   1955  /*
   1956   * Range check input...
   1957   */
   1958 
   1959   if (!ipp || !attr || !*attr ||
   1960       element < 0 || element >= (*attr)->num_values || count <= 0 ||
   1961       (element + count) >= (*attr)->num_values)
   1962     return (0);
   1963 
   1964  /*
   1965   * If we are deleting all values, just delete the attribute entirely.
   1966   */
   1967 
   1968   if (count == (*attr)->num_values)
   1969   {
   1970     ippDeleteAttribute(ipp, *attr);
   1971     *attr = NULL;
   1972     return (1);
   1973   }
   1974 
   1975  /*
   1976   * Otherwise free the values in question and return.
   1977   */
   1978 
   1979   ipp_free_values(*attr, element, count);
   1980 
   1981   return (1);
   1982 }
   1983 
   1984 
   1985 /*
   1986  * 'ippFindAttribute()' - Find a named attribute in a request.
   1987  *
   1988  * Starting with CUPS 2.0, the attribute name can contain a hierarchical list
   1989  * of attribute and member names separated by slashes, for example
   1990  * "media-col/media-size".
   1991  */
   1992 
   1993 ipp_attribute_t	*			/* O - Matching attribute */
   1994 ippFindAttribute(ipp_t      *ipp,	/* I - IPP message */
   1995                  const char *name,	/* I - Name of attribute */
   1996 		 ipp_tag_t  type)	/* I - Type of attribute */
   1997 {
   1998   DEBUG_printf(("2ippFindAttribute(ipp=%p, name=\"%s\", type=%02x(%s))", (void *)ipp, name, type, ippTagString(type)));
   1999 
   2000   if (!ipp || !name)
   2001     return (NULL);
   2002 
   2003  /*
   2004   * Reset the current pointer...
   2005   */
   2006 
   2007   ipp->current = NULL;
   2008   ipp->atend   = 0;
   2009 
   2010  /*
   2011   * Search for the attribute...
   2012   */
   2013 
   2014   return (ippFindNextAttribute(ipp, name, type));
   2015 }
   2016 
   2017 
   2018 /*
   2019  * 'ippFindNextAttribute()' - Find the next named attribute in a request.
   2020  *
   2021  * Starting with CUPS 2.0, the attribute name can contain a hierarchical list
   2022  * of attribute and member names separated by slashes, for example
   2023  * "media-col/media-size".
   2024  */
   2025 
   2026 ipp_attribute_t	*			/* O - Matching attribute */
   2027 ippFindNextAttribute(ipp_t      *ipp,	/* I - IPP message */
   2028                      const char *name,	/* I - Name of attribute */
   2029 		     ipp_tag_t  type)	/* I - Type of attribute */
   2030 {
   2031   ipp_attribute_t	*attr,		/* Current atttribute */
   2032 			*childattr;	/* Child attribute */
   2033   ipp_tag_t		value_tag;	/* Value tag */
   2034   char			parent[1024],	/* Parent attribute name */
   2035 			*child = NULL;	/* Child attribute name */
   2036 
   2037 
   2038   DEBUG_printf(("2ippFindNextAttribute(ipp=%p, name=\"%s\", type=%02x(%s))", (void *)ipp, name, type, ippTagString(type)));
   2039 
   2040   if (!ipp || !name)
   2041     return (NULL);
   2042 
   2043   DEBUG_printf(("3ippFindNextAttribute: atend=%d", ipp->atend));
   2044 
   2045   if (ipp->atend)
   2046     return (NULL);
   2047 
   2048   if (strchr(name, '/'))
   2049   {
   2050    /*
   2051     * Search for child attribute...
   2052     */
   2053 
   2054     strlcpy(parent, name, sizeof(parent));
   2055     if ((child = strchr(parent, '/')) == NULL)
   2056     {
   2057       DEBUG_puts("3ippFindNextAttribute: Attribute name too long.");
   2058       return (NULL);
   2059     }
   2060 
   2061     *child++ = '\0';
   2062 
   2063     if (ipp->current && ipp->current->name && ipp->current->value_tag == IPP_TAG_BEGIN_COLLECTION && !strcmp(parent, ipp->current->name))
   2064     {
   2065       while (ipp->curindex < ipp->current->num_values)
   2066       {
   2067         if ((childattr = ippFindNextAttribute(ipp->current->values[ipp->curindex].collection, child, type)) != NULL)
   2068           return (childattr);
   2069 
   2070         ipp->curindex ++;
   2071         if (ipp->curindex < ipp->current->num_values && ipp->current->values[ipp->curindex].collection)
   2072           ipp->current->values[ipp->curindex].collection->current = NULL;
   2073       }
   2074 
   2075       ipp->prev     = ipp->current;
   2076       ipp->current  = ipp->current->next;
   2077       ipp->curindex = 0;
   2078 
   2079       if (!ipp->current)
   2080       {
   2081         ipp->atend = 1;
   2082         return (NULL);
   2083       }
   2084     }
   2085 
   2086     if (!ipp->current)
   2087     {
   2088       ipp->prev     = NULL;
   2089       ipp->current  = ipp->attrs;
   2090       ipp->curindex = 0;
   2091     }
   2092 
   2093     name = parent;
   2094     attr = ipp->current;
   2095   }
   2096   else if (ipp->current)
   2097   {
   2098     ipp->prev = ipp->current;
   2099     attr      = ipp->current->next;
   2100   }
   2101   else
   2102   {
   2103     ipp->prev = NULL;
   2104     attr      = ipp->attrs;
   2105   }
   2106 
   2107   for (; attr != NULL; ipp->prev = attr, attr = attr->next)
   2108   {
   2109     DEBUG_printf(("4ippFindAttribute: attr=%p, name=\"%s\"", (void *)attr, attr->name));
   2110 
   2111     value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_CUPS_MASK);
   2112 
   2113     if (attr->name != NULL && _cups_strcasecmp(attr->name, name) == 0 &&
   2114         (value_tag == type || type == IPP_TAG_ZERO || name == parent ||
   2115 	 (value_tag == IPP_TAG_TEXTLANG && type == IPP_TAG_TEXT) ||
   2116 	 (value_tag == IPP_TAG_NAMELANG && type == IPP_TAG_NAME)))
   2117     {
   2118       ipp->current = attr;
   2119 
   2120       if (name == parent && attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
   2121       {
   2122         int i;				/* Looping var */
   2123 
   2124         for (i = 0; i < attr->num_values; i ++)
   2125         {
   2126 	  if ((childattr = ippFindAttribute(attr->values[i].collection, child, type)) != NULL)
   2127 	  {
   2128 	    attr->values[0].collection->curindex = i;
   2129 	    return (childattr);
   2130 	  }
   2131         }
   2132       }
   2133       else
   2134         return (attr);
   2135     }
   2136   }
   2137 
   2138   ipp->current = NULL;
   2139   ipp->prev    = NULL;
   2140   ipp->atend   = 1;
   2141 
   2142   return (NULL);
   2143 }
   2144 
   2145 
   2146 /*
   2147  * 'ippFirstAttribute()' - Return the first attribute in the message.
   2148  *
   2149  * @since CUPS 1.6/macOS 10.8@
   2150  */
   2151 
   2152 ipp_attribute_t	*			/* O - First attribute or @code NULL@ if none */
   2153 ippFirstAttribute(ipp_t *ipp)		/* I - IPP message */
   2154 {
   2155  /*
   2156   * Range check input...
   2157   */
   2158 
   2159   if (!ipp)
   2160     return (NULL);
   2161 
   2162  /*
   2163   * Return the first attribute...
   2164   */
   2165 
   2166   return (ipp->current = ipp->attrs);
   2167 }
   2168 
   2169 
   2170 /*
   2171  * 'ippGetBoolean()' - Get a boolean value for an attribute.
   2172  *
   2173  * The @code element@ parameter specifies which value to get from 0 to
   2174  * @code ippGetCount(attr)@ - 1.
   2175  *
   2176  * @since CUPS 1.6/macOS 10.8@
   2177  */
   2178 
   2179 int					/* O - Boolean value or 0 on error */
   2180 ippGetBoolean(ipp_attribute_t *attr,	/* I - IPP attribute */
   2181               int             element)	/* I - Value number (0-based) */
   2182 {
   2183  /*
   2184   * Range check input...
   2185   */
   2186 
   2187   if (!attr || attr->value_tag != IPP_TAG_BOOLEAN ||
   2188       element < 0 || element >= attr->num_values)
   2189     return (0);
   2190 
   2191  /*
   2192   * Return the value...
   2193   */
   2194 
   2195   return (attr->values[element].boolean);
   2196 }
   2197 
   2198 
   2199 /*
   2200  * 'ippGetCollection()' - Get a collection value for an attribute.
   2201  *
   2202  * The @code element@ parameter specifies which value to get from 0 to
   2203  * @code ippGetCount(attr)@ - 1.
   2204  *
   2205  * @since CUPS 1.6/macOS 10.8@
   2206  */
   2207 
   2208 ipp_t *					/* O - Collection value or @code NULL@ on error */
   2209 ippGetCollection(
   2210     ipp_attribute_t *attr,		/* I - IPP attribute */
   2211     int             element)		/* I - Value number (0-based) */
   2212 {
   2213  /*
   2214   * Range check input...
   2215   */
   2216 
   2217   if (!attr || attr->value_tag != IPP_TAG_BEGIN_COLLECTION ||
   2218       element < 0 || element >= attr->num_values)
   2219     return (NULL);
   2220 
   2221  /*
   2222   * Return the value...
   2223   */
   2224 
   2225   return (attr->values[element].collection);
   2226 }
   2227 
   2228 
   2229 /*
   2230  * 'ippGetCount()' - Get the number of values in an attribute.
   2231  *
   2232  * @since CUPS 1.6/macOS 10.8@
   2233  */
   2234 
   2235 int					/* O - Number of values or 0 on error */
   2236 ippGetCount(ipp_attribute_t *attr)	/* I - IPP attribute */
   2237 {
   2238  /*
   2239   * Range check input...
   2240   */
   2241 
   2242   if (!attr)
   2243     return (0);
   2244 
   2245  /*
   2246   * Return the number of values...
   2247   */
   2248 
   2249   return (attr->num_values);
   2250 }
   2251 
   2252 
   2253 /*
   2254  * 'ippGetDate()' - Get a dateTime value for an attribute.
   2255  *
   2256  * The @code element@ parameter specifies which value to get from 0 to
   2257  * @code ippGetCount(attr)@ - 1.
   2258  *
   2259  * @since CUPS 1.6/macOS 10.8@
   2260  */
   2261 
   2262 const ipp_uchar_t *			/* O - dateTime value or @code NULL@ */
   2263 ippGetDate(ipp_attribute_t *attr,	/* I - IPP attribute */
   2264            int             element)	/* I - Value number (0-based) */
   2265 {
   2266  /*
   2267   * Range check input...
   2268   */
   2269 
   2270   if (!attr || attr->value_tag != IPP_TAG_DATE ||
   2271       element < 0 || element >= attr->num_values)
   2272     return (NULL);
   2273 
   2274  /*
   2275   * Return the value...
   2276   */
   2277 
   2278   return (attr->values[element].date);
   2279 }
   2280 
   2281 
   2282 /*
   2283  * 'ippGetGroupTag()' - Get the group associated with an attribute.
   2284  *
   2285  * @since CUPS 1.6/macOS 10.8@
   2286  */
   2287 
   2288 ipp_tag_t				/* O - Group tag or @code IPP_TAG_ZERO@ on error */
   2289 ippGetGroupTag(ipp_attribute_t *attr)	/* I - IPP attribute */
   2290 {
   2291  /*
   2292   * Range check input...
   2293   */
   2294 
   2295   if (!attr)
   2296     return (IPP_TAG_ZERO);
   2297 
   2298  /*
   2299   * Return the group...
   2300   */
   2301 
   2302   return (attr->group_tag);
   2303 }
   2304 
   2305 
   2306 /*
   2307  * 'ippGetInteger()' - Get the integer/enum value for an attribute.
   2308  *
   2309  * The @code element@ parameter specifies which value to get from 0 to
   2310  * @code ippGetCount(attr)@ - 1.
   2311  *
   2312  * @since CUPS 1.6/macOS 10.8@
   2313  */
   2314 
   2315 int					/* O - Value or 0 on error */
   2316 ippGetInteger(ipp_attribute_t *attr,	/* I - IPP attribute */
   2317               int             element)	/* I - Value number (0-based) */
   2318 {
   2319  /*
   2320   * Range check input...
   2321   */
   2322 
   2323   if (!attr || (attr->value_tag != IPP_TAG_INTEGER && attr->value_tag != IPP_TAG_ENUM) ||
   2324       element < 0 || element >= attr->num_values)
   2325     return (0);
   2326 
   2327  /*
   2328   * Return the value...
   2329   */
   2330 
   2331   return (attr->values[element].integer);
   2332 }
   2333 
   2334 
   2335 /*
   2336  * 'ippGetName()' - Get the attribute name.
   2337  *
   2338  * @since CUPS 1.6/macOS 10.8@
   2339  */
   2340 
   2341 const char *				/* O - Attribute name or @code NULL@ for separators */
   2342 ippGetName(ipp_attribute_t *attr)	/* I - IPP attribute */
   2343 {
   2344  /*
   2345   * Range check input...
   2346   */
   2347 
   2348   if (!attr)
   2349     return (NULL);
   2350 
   2351  /*
   2352   * Return the name...
   2353   */
   2354 
   2355   return (attr->name);
   2356 }
   2357 
   2358 
   2359 /*
   2360  * 'ippGetOctetString()' - Get an octetString value from an IPP attribute.
   2361  *
   2362  * The @code element@ parameter specifies which value to get from 0 to
   2363  * @code ippGetCount(attr)@ - 1.
   2364  *
   2365  * @since CUPS 1.7/macOS 10.9@
   2366  */
   2367 
   2368 void *					/* O - Pointer to octetString data */
   2369 ippGetOctetString(
   2370     ipp_attribute_t *attr,		/* I - IPP attribute */
   2371     int             element,		/* I - Value number (0-based) */
   2372     int             *datalen)		/* O - Length of octetString data */
   2373 {
   2374  /*
   2375   * Range check input...
   2376   */
   2377 
   2378   if (!attr || attr->value_tag != IPP_TAG_STRING ||
   2379       element < 0 || element >= attr->num_values)
   2380   {
   2381     if (datalen)
   2382       *datalen = 0;
   2383 
   2384     return (NULL);
   2385   }
   2386 
   2387  /*
   2388   * Return the values...
   2389   */
   2390 
   2391   if (datalen)
   2392     *datalen = attr->values[element].unknown.length;
   2393 
   2394   return (attr->values[element].unknown.data);
   2395 }
   2396 
   2397 
   2398 /*
   2399  * 'ippGetOperation()' - Get the operation ID in an IPP message.
   2400  *
   2401  * @since CUPS 1.6/macOS 10.8@
   2402  */
   2403 
   2404 ipp_op_t				/* O - Operation ID or 0 on error */
   2405 ippGetOperation(ipp_t *ipp)		/* I - IPP request message */
   2406 {
   2407  /*
   2408   * Range check input...
   2409   */
   2410 
   2411   if (!ipp)
   2412     return ((ipp_op_t)0);
   2413 
   2414  /*
   2415   * Return the value...
   2416   */
   2417 
   2418   return (ipp->request.op.operation_id);
   2419 }
   2420 
   2421 
   2422 /*
   2423  * 'ippGetRange()' - Get a rangeOfInteger value from an attribute.
   2424  *
   2425  * The @code element@ parameter specifies which value to get from 0 to
   2426  * @code ippGetCount(attr)@ - 1.
   2427  *
   2428  * @since CUPS 1.6/macOS 10.8@
   2429  */
   2430 
   2431 int					/* O - Lower value of range or 0 */
   2432 ippGetRange(ipp_attribute_t *attr,	/* I - IPP attribute */
   2433 	    int             element,	/* I - Value number (0-based) */
   2434 	    int             *uppervalue)/* O - Upper value of range */
   2435 {
   2436  /*
   2437   * Range check input...
   2438   */
   2439 
   2440   if (!attr || attr->value_tag != IPP_TAG_RANGE ||
   2441       element < 0 || element >= attr->num_values)
   2442   {
   2443     if (uppervalue)
   2444       *uppervalue = 0;
   2445 
   2446     return (0);
   2447   }
   2448 
   2449  /*
   2450   * Return the values...
   2451   */
   2452 
   2453   if (uppervalue)
   2454     *uppervalue = attr->values[element].range.upper;
   2455 
   2456   return (attr->values[element].range.lower);
   2457 }
   2458 
   2459 
   2460 /*
   2461  * 'ippGetRequestId()' - Get the request ID from an IPP message.
   2462  *
   2463  * @since CUPS 1.6/macOS 10.8@
   2464  */
   2465 
   2466 int					/* O - Request ID or 0 on error */
   2467 ippGetRequestId(ipp_t *ipp)		/* I - IPP message */
   2468 {
   2469  /*
   2470   * Range check input...
   2471   */
   2472 
   2473   if (!ipp)
   2474     return (0);
   2475 
   2476  /*
   2477   * Return the request ID...
   2478   */
   2479 
   2480   return (ipp->request.any.request_id);
   2481 }
   2482 
   2483 
   2484 /*
   2485  * 'ippGetResolution()' - Get a resolution value for an attribute.
   2486  *
   2487  * The @code element@ parameter specifies which value to get from 0 to
   2488  * @code ippGetCount(attr)@ - 1.
   2489  *
   2490  * @since CUPS 1.6/macOS 10.8@
   2491  */
   2492 
   2493 int					/* O - Horizontal/cross feed resolution or 0 */
   2494 ippGetResolution(
   2495     ipp_attribute_t *attr,		/* I - IPP attribute */
   2496     int             element,		/* I - Value number (0-based) */
   2497     int             *yres,		/* O - Vertical/feed resolution */
   2498     ipp_res_t       *units)		/* O - Units for resolution */
   2499 {
   2500  /*
   2501   * Range check input...
   2502   */
   2503 
   2504   if (!attr || attr->value_tag != IPP_TAG_RESOLUTION ||
   2505       element < 0 || element >= attr->num_values)
   2506   {
   2507     if (yres)
   2508       *yres = 0;
   2509 
   2510     if (units)
   2511       *units = (ipp_res_t)0;
   2512 
   2513     return (0);
   2514   }
   2515 
   2516  /*
   2517   * Return the value...
   2518   */
   2519 
   2520   if (yres)
   2521     *yres = attr->values[element].resolution.yres;
   2522 
   2523   if (units)
   2524     *units = attr->values[element].resolution.units;
   2525 
   2526   return (attr->values[element].resolution.xres);
   2527 }
   2528 
   2529 
   2530 /*
   2531  * 'ippGetState()' - Get the IPP message state.
   2532  *
   2533  * @since CUPS 1.6/macOS 10.8@
   2534  */
   2535 
   2536 ipp_state_t				/* O - IPP message state value */
   2537 ippGetState(ipp_t *ipp)			/* I - IPP message */
   2538 {
   2539  /*
   2540   * Range check input...
   2541   */
   2542 
   2543   if (!ipp)
   2544     return (IPP_STATE_IDLE);
   2545 
   2546  /*
   2547   * Return the value...
   2548   */
   2549 
   2550   return (ipp->state);
   2551 }
   2552 
   2553 
   2554 /*
   2555  * 'ippGetStatusCode()' - Get the status code from an IPP response or event message.
   2556  *
   2557  * @since CUPS 1.6/macOS 10.8@
   2558  */
   2559 
   2560 ipp_status_t				/* O - Status code in IPP message */
   2561 ippGetStatusCode(ipp_t *ipp)		/* I - IPP response or event message */
   2562 {
   2563  /*
   2564   * Range check input...
   2565   */
   2566 
   2567   if (!ipp)
   2568     return (IPP_STATUS_ERROR_INTERNAL);
   2569 
   2570  /*
   2571   * Return the value...
   2572   */
   2573 
   2574   return (ipp->request.status.status_code);
   2575 }
   2576 
   2577 
   2578 /*
   2579  * 'ippGetString()' - Get the string and optionally the language code for an attribute.
   2580  *
   2581  * The @code element@ parameter specifies which value to get from 0 to
   2582  * @code ippGetCount(attr)@ - 1.
   2583  *
   2584  * @since CUPS 1.6/macOS 10.8@
   2585  */
   2586 
   2587 const char *
   2588 ippGetString(ipp_attribute_t *attr,	/* I - IPP attribute */
   2589              int             element,	/* I - Value number (0-based) */
   2590 	     const char      **language)/* O - Language code (@code NULL@ for don't care) */
   2591 {
   2592   ipp_tag_t	tag;			/* Value tag */
   2593 
   2594 
   2595  /*
   2596   * Range check input...
   2597   */
   2598 
   2599   tag = ippGetValueTag(attr);
   2600 
   2601   if (!attr || element < 0 || element >= attr->num_values || (tag != IPP_TAG_TEXTLANG && tag != IPP_TAG_NAMELANG && (tag < IPP_TAG_TEXT || tag > IPP_TAG_MIMETYPE)))
   2602     return (NULL);
   2603 
   2604  /*
   2605   * Return the value...
   2606   */
   2607 
   2608   if (language)
   2609     *language = attr->values[element].string.language;
   2610 
   2611   return (attr->values[element].string.text);
   2612 }
   2613 
   2614 
   2615 /*
   2616  * 'ippGetValueTag()' - Get the value tag for an attribute.
   2617  *
   2618  * @since CUPS 1.6/macOS 10.8@
   2619  */
   2620 
   2621 ipp_tag_t				/* O - Value tag or @code IPP_TAG_ZERO@ on error */
   2622 ippGetValueTag(ipp_attribute_t *attr)	/* I - IPP attribute */
   2623 {
   2624  /*
   2625   * Range check input...
   2626   */
   2627 
   2628   if (!attr)
   2629     return (IPP_TAG_ZERO);
   2630 
   2631  /*
   2632   * Return the value...
   2633   */
   2634 
   2635   return (attr->value_tag & IPP_TAG_CUPS_MASK);
   2636 }
   2637 
   2638 
   2639 /*
   2640  * 'ippGetVersion()' - Get the major and minor version number from an IPP message.
   2641  *
   2642  * @since CUPS 1.6/macOS 10.8@
   2643  */
   2644 
   2645 int					/* O - Major version number or 0 on error */
   2646 ippGetVersion(ipp_t *ipp,		/* I - IPP message */
   2647               int   *minor)		/* O - Minor version number or @code NULL@ for don't care */
   2648 {
   2649  /*
   2650   * Range check input...
   2651   */
   2652 
   2653   if (!ipp)
   2654   {
   2655     if (minor)
   2656       *minor = 0;
   2657 
   2658     return (0);
   2659   }
   2660 
   2661  /*
   2662   * Return the value...
   2663   */
   2664 
   2665   if (minor)
   2666     *minor = ipp->request.any.version[1];
   2667 
   2668   return (ipp->request.any.version[0]);
   2669 }
   2670 
   2671 
   2672 /*
   2673  * 'ippLength()' - Compute the length of an IPP message.
   2674  */
   2675 
   2676 size_t					/* O - Size of IPP message */
   2677 ippLength(ipp_t *ipp)			/* I - IPP message */
   2678 {
   2679   return (ipp_length(ipp, 0));
   2680 }
   2681 
   2682 
   2683 /*
   2684  * 'ippNextAttribute()' - Return the next attribute in the message.
   2685  *
   2686  * @since CUPS 1.6/macOS 10.8@
   2687  */
   2688 
   2689 ipp_attribute_t *			/* O - Next attribute or @code NULL@ if none */
   2690 ippNextAttribute(ipp_t *ipp)		/* I - IPP message */
   2691 {
   2692  /*
   2693   * Range check input...
   2694   */
   2695 
   2696   if (!ipp || !ipp->current)
   2697     return (NULL);
   2698 
   2699  /*
   2700   * Return the next attribute...
   2701   */
   2702 
   2703   return (ipp->current = ipp->current->next);
   2704 }
   2705 
   2706 
   2707 /*
   2708  * 'ippNew()' - Allocate a new IPP message.
   2709  */
   2710 
   2711 ipp_t *					/* O - New IPP message */
   2712 ippNew(void)
   2713 {
   2714   ipp_t			*temp;		/* New IPP message */
   2715   _cups_globals_t	*cg = _cupsGlobals();
   2716 					/* Global data */
   2717 
   2718 
   2719   DEBUG_puts("ippNew()");
   2720 
   2721   if ((temp = (ipp_t *)calloc(1, sizeof(ipp_t))) != NULL)
   2722   {
   2723    /*
   2724     * Set default version - usually 2.0...
   2725     */
   2726 
   2727     DEBUG_printf(("4debug_alloc: %p IPP message", (void *)temp));
   2728 
   2729     if (cg->server_version == 0)
   2730       _cupsSetDefaults();
   2731 
   2732     temp->request.any.version[0] = (ipp_uchar_t)(cg->server_version / 10);
   2733     temp->request.any.version[1] = (ipp_uchar_t)(cg->server_version % 10);
   2734     temp->use                    = 1;
   2735   }
   2736 
   2737   DEBUG_printf(("1ippNew: Returning %p", (void *)temp));
   2738 
   2739   return (temp);
   2740 }
   2741 
   2742 
   2743 /*
   2744  *  'ippNewRequest()' - Allocate a new IPP request message.
   2745  *
   2746  * The new request message is initialized with the "attributes-charset" and
   2747  * "attributes-natural-language" attributes added. The
   2748  * "attributes-natural-language" value is derived from the current locale.
   2749  *
   2750  * @since CUPS 1.2/macOS 10.5@
   2751  */
   2752 
   2753 ipp_t *					/* O - IPP request message */
   2754 ippNewRequest(ipp_op_t op)		/* I - Operation code */
   2755 {
   2756   ipp_t		*request;		/* IPP request message */
   2757   cups_lang_t	*language;		/* Current language localization */
   2758   static int	request_id = 0;		/* Current request ID */
   2759   static _cups_mutex_t request_mutex = _CUPS_MUTEX_INITIALIZER;
   2760 					/* Mutex for request ID */
   2761 
   2762 
   2763   DEBUG_printf(("ippNewRequest(op=%02x(%s))", op, ippOpString(op)));
   2764 
   2765  /*
   2766   * Create a new IPP message...
   2767   */
   2768 
   2769   if ((request = ippNew()) == NULL)
   2770     return (NULL);
   2771 
   2772  /*
   2773   * Set the operation and request ID...
   2774   */
   2775 
   2776   _cupsMutexLock(&request_mutex);
   2777 
   2778   request->request.op.operation_id = op;
   2779   request->request.op.request_id   = ++request_id;
   2780 
   2781   _cupsMutexUnlock(&request_mutex);
   2782 
   2783  /*
   2784   * Use UTF-8 as the character set...
   2785   */
   2786 
   2787   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
   2788                "attributes-charset", NULL, "utf-8");
   2789 
   2790  /*
   2791   * Get the language from the current locale...
   2792   */
   2793 
   2794   language = cupsLangDefault();
   2795 
   2796   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
   2797                "attributes-natural-language", NULL, language->language);
   2798 
   2799  /*
   2800   * Return the new request...
   2801   */
   2802 
   2803   return (request);
   2804 }
   2805 
   2806 
   2807 /*
   2808  * 'ippNewResponse()' - Allocate a new IPP response message.
   2809  *
   2810  * The new response message is initialized with the same "version-number",
   2811  * "request-id", "attributes-charset", and "attributes-natural-language" as the
   2812  * provided request message.  If the "attributes-charset" or
   2813  * "attributes-natural-language" attributes are missing from the request,
   2814  * 'utf-8' and a value derived from the current locale are substituted,
   2815  * respectively.
   2816  *
   2817  * @since CUPS 1.7/macOS 10.9@
   2818  */
   2819 
   2820 ipp_t *					/* O - IPP response message */
   2821 ippNewResponse(ipp_t *request)		/* I - IPP request message */
   2822 {
   2823   ipp_t			*response;	/* IPP response message */
   2824   ipp_attribute_t	*attr;		/* Current attribute */
   2825 
   2826 
   2827  /*
   2828   * Range check input...
   2829   */
   2830 
   2831   if (!request)
   2832     return (NULL);
   2833 
   2834  /*
   2835   * Create a new IPP message...
   2836   */
   2837 
   2838   if ((response = ippNew()) == NULL)
   2839     return (NULL);
   2840 
   2841  /*
   2842   * Copy the request values over to the response...
   2843   */
   2844 
   2845   response->request.status.version[0] = request->request.op.version[0];
   2846   response->request.status.version[1] = request->request.op.version[1];
   2847   response->request.status.request_id = request->request.op.request_id;
   2848 
   2849  /*
   2850   * The first attribute MUST be attributes-charset...
   2851   */
   2852 
   2853   attr = request->attrs;
   2854 
   2855   if (attr && attr->name && !strcmp(attr->name, "attributes-charset") &&
   2856       attr->group_tag == IPP_TAG_OPERATION &&
   2857       attr->value_tag == IPP_TAG_CHARSET &&
   2858       attr->num_values == 1)
   2859   {
   2860    /*
   2861     * Copy charset from request...
   2862     */
   2863 
   2864     ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
   2865 		 "attributes-charset", NULL, attr->values[0].string.text);
   2866   }
   2867   else
   2868   {
   2869    /*
   2870     * Use "utf-8" as the default...
   2871     */
   2872 
   2873     ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
   2874 		 "attributes-charset", NULL, "utf-8");
   2875   }
   2876 
   2877  /*
   2878   * Then attributes-natural-language...
   2879   */
   2880 
   2881   if (attr)
   2882     attr = attr->next;
   2883 
   2884   if (attr && attr->name &&
   2885       !strcmp(attr->name, "attributes-natural-language") &&
   2886       attr->group_tag == IPP_TAG_OPERATION &&
   2887       attr->value_tag == IPP_TAG_LANGUAGE &&
   2888       attr->num_values == 1)
   2889   {
   2890    /*
   2891     * Copy language from request...
   2892     */
   2893 
   2894     ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
   2895 		 "attributes-natural-language", NULL,
   2896 		 attr->values[0].string.text);
   2897   }
   2898   else
   2899   {
   2900    /*
   2901     * Use the language from the current locale...
   2902     */
   2903 
   2904     cups_lang_t *language = cupsLangDefault();
   2905 					/* Current locale */
   2906 
   2907     ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
   2908 		 "attributes-natural-language", NULL, language->language);
   2909   }
   2910 
   2911   return (response);
   2912 }
   2913 
   2914 
   2915 /*
   2916  * 'ippRead()' - Read data for an IPP message from a HTTP connection.
   2917  */
   2918 
   2919 ipp_state_t				/* O - Current state */
   2920 ippRead(http_t *http,			/* I - HTTP connection */
   2921         ipp_t  *ipp)			/* I - IPP data */
   2922 {
   2923   DEBUG_printf(("ippRead(http=%p, ipp=%p), data_remaining=" CUPS_LLFMT, (void *)http, (void *)ipp, CUPS_LLCAST (http ? http->data_remaining : -1)));
   2924 
   2925   if (!http)
   2926     return (IPP_STATE_ERROR);
   2927 
   2928   DEBUG_printf(("2ippRead: http->state=%d, http->used=%d", http->state, http->used));
   2929 
   2930   return (ippReadIO(http, (ipp_iocb_t)ipp_read_http, http->blocking, NULL,
   2931                     ipp));
   2932 }
   2933 
   2934 
   2935 /*
   2936  * 'ippReadFile()' - Read data for an IPP message from a file.
   2937  *
   2938  * @since CUPS 1.1.19/macOS 10.3@
   2939  */
   2940 
   2941 ipp_state_t				/* O - Current state */
   2942 ippReadFile(int   fd,			/* I - HTTP data */
   2943             ipp_t *ipp)			/* I - IPP data */
   2944 {
   2945   DEBUG_printf(("ippReadFile(fd=%d, ipp=%p)", fd, (void *)ipp));
   2946 
   2947   return (ippReadIO(&fd, (ipp_iocb_t)ipp_read_file, 1, NULL, ipp));
   2948 }
   2949 
   2950 
   2951 /*
   2952  * 'ippReadIO()' - Read data for an IPP message.
   2953  *
   2954  * @since CUPS 1.2/macOS 10.5@
   2955  */
   2956 
   2957 ipp_state_t				/* O - Current state */
   2958 ippReadIO(void       *src,		/* I - Data source */
   2959           ipp_iocb_t cb,		/* I - Read callback function */
   2960 	  int        blocking,		/* I - Use blocking IO? */
   2961 	  ipp_t      *parent,		/* I - Parent request, if any */
   2962           ipp_t      *ipp)		/* I - IPP data */
   2963 {
   2964   int			n;		/* Length of data */
   2965   unsigned char		*buffer,	/* Data buffer */
   2966 			string[IPP_MAX_TEXT],
   2967 					/* Small string buffer */
   2968 			*bufptr;	/* Pointer into buffer */
   2969   ipp_attribute_t	*attr;		/* Current attribute */
   2970   ipp_tag_t		tag;		/* Current tag */
   2971   ipp_tag_t		value_tag;	/* Current value tag */
   2972   _ipp_value_t		*value;		/* Current value */
   2973 
   2974 
   2975   DEBUG_printf(("ippReadIO(src=%p, cb=%p, blocking=%d, parent=%p, ipp=%p)", (void *)src, (void *)cb, blocking, (void *)parent, (void *)ipp));
   2976   DEBUG_printf(("2ippReadIO: ipp->state=%d", ipp ? ipp->state : IPP_STATE_ERROR));
   2977 
   2978   if (!src || !ipp)
   2979     return (IPP_STATE_ERROR);
   2980 
   2981   if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL)
   2982   {
   2983     DEBUG_puts("1ippReadIO: Unable to get read buffer.");
   2984     return (IPP_STATE_ERROR);
   2985   }
   2986 
   2987   switch (ipp->state)
   2988   {
   2989     case IPP_STATE_IDLE :
   2990         ipp->state ++; /* Avoid common problem... */
   2991 
   2992     case IPP_STATE_HEADER :
   2993         if (parent == NULL)
   2994 	{
   2995 	 /*
   2996           * Get the request header...
   2997 	  */
   2998 
   2999           if ((*cb)(src, buffer, 8) < 8)
   3000 	  {
   3001 	    DEBUG_puts("1ippReadIO: Unable to read header.");
   3002 	    _cupsBufferRelease((char *)buffer);
   3003 	    return (IPP_STATE_ERROR);
   3004 	  }
   3005 
   3006 	 /*
   3007           * Then copy the request header over...
   3008 	  */
   3009 
   3010           ipp->request.any.version[0]  = buffer[0];
   3011           ipp->request.any.version[1]  = buffer[1];
   3012           ipp->request.any.op_status   = (buffer[2] << 8) | buffer[3];
   3013           ipp->request.any.request_id  = (((((buffer[4] << 8) | buffer[5]) << 8) |
   3014 	                        	 buffer[6]) << 8) | buffer[7];
   3015 
   3016           DEBUG_printf(("2ippReadIO: version=%d.%d", buffer[0], buffer[1]));
   3017 	  DEBUG_printf(("2ippReadIO: op_status=%04x",
   3018 	                ipp->request.any.op_status));
   3019 	  DEBUG_printf(("2ippReadIO: request_id=%d",
   3020 	                ipp->request.any.request_id));
   3021         }
   3022 
   3023         ipp->state   = IPP_STATE_ATTRIBUTE;
   3024 	ipp->current = NULL;
   3025 	ipp->curtag  = IPP_TAG_ZERO;
   3026 	ipp->prev    = ipp->last;
   3027 
   3028        /*
   3029         * If blocking is disabled, stop here...
   3030 	*/
   3031 
   3032         if (!blocking)
   3033 	  break;
   3034 
   3035     case IPP_STATE_ATTRIBUTE :
   3036         for (;;)
   3037 	{
   3038 	  if ((*cb)(src, buffer, 1) < 1)
   3039 	  {
   3040 	    DEBUG_puts("1ippReadIO: Callback returned EOF/error");
   3041 	    _cupsBufferRelease((char *)buffer);
   3042 	    return (IPP_STATE_ERROR);
   3043 	  }
   3044 
   3045 	  DEBUG_printf(("2ippReadIO: ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev));
   3046 
   3047 	 /*
   3048 	  * Read this attribute...
   3049 	  */
   3050 
   3051           tag = (ipp_tag_t)buffer[0];
   3052           if (tag == IPP_TAG_EXTENSION)
   3053           {
   3054            /*
   3055             * Read 32-bit "extension" tag...
   3056             */
   3057 
   3058 	    if ((*cb)(src, buffer, 4) < 1)
   3059 	    {
   3060 	      DEBUG_puts("1ippReadIO: Callback returned EOF/error");
   3061 	      _cupsBufferRelease((char *)buffer);
   3062 	      return (IPP_STATE_ERROR);
   3063 	    }
   3064 
   3065 	    tag = (ipp_tag_t)((((((buffer[0] << 8) | buffer[1]) << 8) |
   3066 	                        buffer[2]) << 8) | buffer[3]);
   3067 
   3068             if (tag & IPP_TAG_CUPS_CONST)
   3069             {
   3070              /*
   3071               * Fail if the high bit is set in the tag...
   3072               */
   3073 
   3074 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP extension tag larger than 0x7FFFFFFF."), 1);
   3075 	      DEBUG_printf(("1ippReadIO: bad tag 0x%x.", tag));
   3076 	      _cupsBufferRelease((char *)buffer);
   3077 	      return (IPP_STATE_ERROR);
   3078             }
   3079           }
   3080 
   3081 	  if (tag == IPP_TAG_END)
   3082 	  {
   3083 	   /*
   3084 	    * No more attributes left...
   3085 	    */
   3086 
   3087             DEBUG_puts("2ippReadIO: IPP_TAG_END.");
   3088 
   3089 	    ipp->state = IPP_STATE_DATA;
   3090 	    break;
   3091 	  }
   3092           else if (tag < IPP_TAG_UNSUPPORTED_VALUE)
   3093 	  {
   3094 	   /*
   3095 	    * Group tag...  Set the current group and continue...
   3096 	    */
   3097 
   3098             if (ipp->curtag == tag)
   3099 	      ipp->prev = ippAddSeparator(ipp);
   3100             else if (ipp->current)
   3101 	      ipp->prev = ipp->current;
   3102 
   3103 	    ipp->curtag  = tag;
   3104 	    ipp->current = NULL;
   3105 	    DEBUG_printf(("2ippReadIO: group tag=%x(%s), ipp->prev=%p", tag, ippTagString(tag), (void *)ipp->prev));
   3106 	    continue;
   3107 	  }
   3108 
   3109           DEBUG_printf(("2ippReadIO: value tag=%x(%s)", tag,
   3110 	                ippTagString(tag)));
   3111 
   3112          /*
   3113 	  * Get the name...
   3114 	  */
   3115 
   3116           if ((*cb)(src, buffer, 2) < 2)
   3117 	  {
   3118 	    DEBUG_puts("1ippReadIO: unable to read name length.");
   3119 	    _cupsBufferRelease((char *)buffer);
   3120 	    return (IPP_STATE_ERROR);
   3121 	  }
   3122 
   3123           n = (buffer[0] << 8) | buffer[1];
   3124 
   3125           if (n >= IPP_BUF_SIZE)
   3126 	  {
   3127 	    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP name larger than 32767 bytes."), 1);
   3128 	    DEBUG_printf(("1ippReadIO: bad name length %d.", n));
   3129 	    _cupsBufferRelease((char *)buffer);
   3130 	    return (IPP_STATE_ERROR);
   3131 	  }
   3132 
   3133           DEBUG_printf(("2ippReadIO: name length=%d", n));
   3134 
   3135           if (n == 0 && tag != IPP_TAG_MEMBERNAME &&
   3136 	      tag != IPP_TAG_END_COLLECTION)
   3137 	  {
   3138 	   /*
   3139 	    * More values for current attribute...
   3140 	    */
   3141 
   3142             if (ipp->current == NULL)
   3143 	    {
   3144 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP attribute has no name."), 1);
   3145 	      DEBUG_puts("1ippReadIO: Attribute without name and no current.");
   3146 	      _cupsBufferRelease((char *)buffer);
   3147 	      return (IPP_STATE_ERROR);
   3148 	    }
   3149 
   3150             attr      = ipp->current;
   3151 	    value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_CUPS_MASK);
   3152 
   3153 	   /*
   3154 	    * Make sure we aren't adding a new value of a different
   3155 	    * type...
   3156 	    */
   3157 
   3158 	    if (value_tag == IPP_TAG_ZERO)
   3159 	    {
   3160 	     /*
   3161 	      * Setting the value of a collection member...
   3162 	      */
   3163 
   3164 	      attr->value_tag = tag;
   3165 	    }
   3166 	    else if (value_tag == IPP_TAG_TEXTLANG ||
   3167 	             value_tag == IPP_TAG_NAMELANG ||
   3168 		     (value_tag >= IPP_TAG_TEXT &&
   3169 		      value_tag <= IPP_TAG_MIMETYPE))
   3170             {
   3171 	     /*
   3172 	      * String values can sometimes come across in different
   3173 	      * forms; accept sets of differing values...
   3174 	      */
   3175 
   3176 	      if (tag != IPP_TAG_TEXTLANG && tag != IPP_TAG_NAMELANG &&
   3177 	          (tag < IPP_TAG_TEXT || tag > IPP_TAG_MIMETYPE) &&
   3178 		  tag != IPP_TAG_NOVALUE)
   3179 	      {
   3180 		_cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3181 		              _("IPP 1setOf attribute with incompatible value "
   3182 		                "tags."), 1);
   3183 		DEBUG_printf(("1ippReadIO: 1setOf value tag %x(%s) != %x(%s)",
   3184 			      value_tag, ippTagString(value_tag), tag,
   3185 			      ippTagString(tag)));
   3186 		_cupsBufferRelease((char *)buffer);
   3187 	        return (IPP_STATE_ERROR);
   3188 	      }
   3189 
   3190               if (value_tag != tag)
   3191               {
   3192                 DEBUG_printf(("1ippReadIO: Converting %s attribute from %s to %s.",
   3193                               attr->name, ippTagString(value_tag), ippTagString(tag)));
   3194 		ippSetValueTag(ipp, &attr, tag);
   3195 	      }
   3196             }
   3197 	    else if (value_tag == IPP_TAG_INTEGER ||
   3198 	             value_tag == IPP_TAG_RANGE)
   3199             {
   3200 	     /*
   3201 	      * Integer and rangeOfInteger values can sometimes be mixed; accept
   3202 	      * sets of differing values...
   3203 	      */
   3204 
   3205 	      if (tag != IPP_TAG_INTEGER && tag != IPP_TAG_RANGE)
   3206 	      {
   3207 		_cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3208 		              _("IPP 1setOf attribute with incompatible value "
   3209 		                "tags."), 1);
   3210 		DEBUG_printf(("1ippReadIO: 1setOf value tag %x(%s) != %x(%s)",
   3211 			      value_tag, ippTagString(value_tag), tag,
   3212 			      ippTagString(tag)));
   3213 		_cupsBufferRelease((char *)buffer);
   3214 	        return (IPP_STATE_ERROR);
   3215 	      }
   3216 
   3217               if (value_tag == IPP_TAG_INTEGER && tag == IPP_TAG_RANGE)
   3218               {
   3219                /*
   3220                 * Convert integer values to rangeOfInteger values...
   3221                 */
   3222 
   3223 		DEBUG_printf(("1ippReadIO: Converting %s attribute to "
   3224 		              "rangeOfInteger.", attr->name));
   3225                 ippSetValueTag(ipp, &attr, IPP_TAG_RANGE);
   3226               }
   3227             }
   3228 	    else if (value_tag != tag)
   3229 	    {
   3230 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3231 			    _("IPP 1setOf attribute with incompatible value "
   3232 			      "tags."), 1);
   3233 	      DEBUG_printf(("1ippReadIO: value tag %x(%s) != %x(%s)",
   3234 	                    value_tag, ippTagString(value_tag), tag,
   3235 			    ippTagString(tag)));
   3236 	      _cupsBufferRelease((char *)buffer);
   3237 	      return (IPP_STATE_ERROR);
   3238             }
   3239 
   3240            /*
   3241 	    * Finally, reallocate the attribute array as needed...
   3242 	    */
   3243 
   3244 	    if ((value = ipp_set_value(ipp, &attr, attr->num_values)) == NULL)
   3245 	    {
   3246 	      _cupsBufferRelease((char *)buffer);
   3247 	      return (IPP_STATE_ERROR);
   3248 	    }
   3249 	  }
   3250 	  else if (tag == IPP_TAG_MEMBERNAME)
   3251 	  {
   3252 	   /*
   3253 	    * Name must be length 0!
   3254 	    */
   3255 
   3256 	    if (n)
   3257 	    {
   3258 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP member name is not empty."), 1);
   3259 	      DEBUG_puts("1ippReadIO: member name not empty.");
   3260 	      _cupsBufferRelease((char *)buffer);
   3261 	      return (IPP_STATE_ERROR);
   3262 	    }
   3263 
   3264             if (ipp->current)
   3265 	      ipp->prev = ipp->current;
   3266 
   3267 	    attr = ipp->current = ipp_add_attr(ipp, NULL, ipp->curtag, IPP_TAG_ZERO, 1);
   3268 	    if (!attr)
   3269 	    {
   3270 	      _cupsSetHTTPError(HTTP_STATUS_ERROR);
   3271 	      DEBUG_puts("1ippReadIO: unable to allocate attribute.");
   3272 	      _cupsBufferRelease((char *)buffer);
   3273 	      return (IPP_STATE_ERROR);
   3274 	    }
   3275 
   3276 	    DEBUG_printf(("2ippReadIO: membername, ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev));
   3277 
   3278 	    value = attr->values;
   3279 	  }
   3280 	  else if (tag != IPP_TAG_END_COLLECTION)
   3281 	  {
   3282 	   /*
   3283 	    * New attribute; read the name and add it...
   3284 	    */
   3285 
   3286 	    if ((*cb)(src, buffer, (size_t)n) < n)
   3287 	    {
   3288 	      DEBUG_puts("1ippReadIO: unable to read name.");
   3289 	      _cupsBufferRelease((char *)buffer);
   3290 	      return (IPP_STATE_ERROR);
   3291 	    }
   3292 
   3293 	    buffer[n] = '\0';
   3294 
   3295             if (ipp->current)
   3296 	      ipp->prev = ipp->current;
   3297 
   3298 	    if ((attr = ipp->current = ipp_add_attr(ipp, (char *)buffer, ipp->curtag, tag,
   3299 	                                            1)) == NULL)
   3300 	    {
   3301 	      _cupsSetHTTPError(HTTP_STATUS_ERROR);
   3302 	      DEBUG_puts("1ippReadIO: unable to allocate attribute.");
   3303 	      _cupsBufferRelease((char *)buffer);
   3304 	      return (IPP_STATE_ERROR);
   3305 	    }
   3306 
   3307 	    DEBUG_printf(("2ippReadIO: name=\"%s\", ipp->current=%p, ipp->prev=%p", buffer, (void *)ipp->current, (void *)ipp->prev));
   3308 
   3309 	    value = attr->values;
   3310 	  }
   3311 	  else
   3312 	  {
   3313 	    attr  = NULL;
   3314 	    value = NULL;
   3315 	  }
   3316 
   3317 	  if ((*cb)(src, buffer, 2) < 2)
   3318 	  {
   3319 	    DEBUG_puts("1ippReadIO: unable to read value length.");
   3320 	    _cupsBufferRelease((char *)buffer);
   3321 	    return (IPP_STATE_ERROR);
   3322 	  }
   3323 
   3324 	  n = (buffer[0] << 8) | buffer[1];
   3325           DEBUG_printf(("2ippReadIO: value length=%d", n));
   3326 
   3327 	  if (n >= IPP_BUF_SIZE)
   3328 	  {
   3329 	    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3330 			  _("IPP value larger than 32767 bytes."), 1);
   3331 	    DEBUG_printf(("1ippReadIO: bad value length %d.", n));
   3332 	    _cupsBufferRelease((char *)buffer);
   3333 	    return (IPP_STATE_ERROR);
   3334 	  }
   3335 
   3336 	  switch (tag)
   3337 	  {
   3338 	    case IPP_TAG_INTEGER :
   3339 	    case IPP_TAG_ENUM :
   3340 		if (n != 4)
   3341 		{
   3342 		  if (tag == IPP_TAG_INTEGER)
   3343 		    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3344 				  _("IPP integer value not 4 bytes."), 1);
   3345 		  else
   3346 		    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3347 				  _("IPP enum value not 4 bytes."), 1);
   3348 		  DEBUG_printf(("1ippReadIO: bad integer value length %d.", n));
   3349 		  _cupsBufferRelease((char *)buffer);
   3350 		  return (IPP_STATE_ERROR);
   3351 		}
   3352 
   3353 	        if ((*cb)(src, buffer, 4) < 4)
   3354 		{
   3355 	          DEBUG_puts("1ippReadIO: Unable to read integer value.");
   3356 		  _cupsBufferRelease((char *)buffer);
   3357 		  return (IPP_STATE_ERROR);
   3358 		}
   3359 
   3360 		n = (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
   3361 		    buffer[3];
   3362 
   3363                 if (attr->value_tag == IPP_TAG_RANGE)
   3364                   value->range.lower = value->range.upper = n;
   3365                 else
   3366 		  value->integer = n;
   3367 	        break;
   3368 
   3369 	    case IPP_TAG_BOOLEAN :
   3370 		if (n != 1)
   3371 		{
   3372 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP boolean value not 1 byte."),
   3373 		                1);
   3374 		  DEBUG_printf(("1ippReadIO: bad boolean value length %d.", n));
   3375 		  _cupsBufferRelease((char *)buffer);
   3376 		  return (IPP_STATE_ERROR);
   3377 		}
   3378 
   3379 	        if ((*cb)(src, buffer, 1) < 1)
   3380 		{
   3381 	          DEBUG_puts("1ippReadIO: Unable to read boolean value.");
   3382 		  _cupsBufferRelease((char *)buffer);
   3383 		  return (IPP_STATE_ERROR);
   3384 		}
   3385 
   3386                 value->boolean = (char)buffer[0];
   3387 	        break;
   3388 
   3389             case IPP_TAG_NOVALUE :
   3390 	    case IPP_TAG_NOTSETTABLE :
   3391 	    case IPP_TAG_DELETEATTR :
   3392 	    case IPP_TAG_ADMINDEFINE :
   3393 	       /*
   3394 	        * These value types are not supposed to have values, however
   3395 		* some vendors (Brother) do not implement IPP correctly and so
   3396 		* we need to map non-empty values to text...
   3397 		*/
   3398 
   3399 	        if (attr->value_tag == tag)
   3400 		{
   3401 		  if (n == 0)
   3402 		    break;
   3403 
   3404 		  attr->value_tag = IPP_TAG_TEXT;
   3405 		}
   3406 
   3407 	    case IPP_TAG_TEXT :
   3408 	    case IPP_TAG_NAME :
   3409 	    case IPP_TAG_KEYWORD :
   3410 	    case IPP_TAG_URI :
   3411 	    case IPP_TAG_URISCHEME :
   3412 	    case IPP_TAG_CHARSET :
   3413 	    case IPP_TAG_LANGUAGE :
   3414 	    case IPP_TAG_MIMETYPE :
   3415 	        if (n > 0)
   3416 	        {
   3417 		  if ((*cb)(src, buffer, (size_t)n) < n)
   3418 		  {
   3419 		    DEBUG_puts("1ippReadIO: unable to read string value.");
   3420 		    _cupsBufferRelease((char *)buffer);
   3421 		    return (IPP_STATE_ERROR);
   3422 		  }
   3423 		}
   3424 
   3425 		buffer[n] = '\0';
   3426 		value->string.text = _cupsStrAlloc((char *)buffer);
   3427 		DEBUG_printf(("2ippReadIO: value=\"%s\"", value->string.text));
   3428 	        break;
   3429 
   3430 	    case IPP_TAG_DATE :
   3431 		if (n != 11)
   3432 		{
   3433 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP date value not 11 bytes."), 1);
   3434 		  DEBUG_printf(("1ippReadIO: bad date value length %d.", n));
   3435 		  _cupsBufferRelease((char *)buffer);
   3436 		  return (IPP_STATE_ERROR);
   3437 		}
   3438 
   3439 	        if ((*cb)(src, value->date, 11) < 11)
   3440 		{
   3441 	          DEBUG_puts("1ippReadIO: Unable to read date value.");
   3442 		  _cupsBufferRelease((char *)buffer);
   3443 		  return (IPP_STATE_ERROR);
   3444 		}
   3445 	        break;
   3446 
   3447 	    case IPP_TAG_RESOLUTION :
   3448 		if (n != 9)
   3449 		{
   3450 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3451 		                _("IPP resolution value not 9 bytes."), 1);
   3452 		  DEBUG_printf(("1ippReadIO: bad resolution value length %d.", n));
   3453 		  _cupsBufferRelease((char *)buffer);
   3454 		  return (IPP_STATE_ERROR);
   3455 		}
   3456 
   3457 	        if ((*cb)(src, buffer, 9) < 9)
   3458 		{
   3459 	          DEBUG_puts("1ippReadIO: Unable to read resolution value.");
   3460 		  _cupsBufferRelease((char *)buffer);
   3461 		  return (IPP_STATE_ERROR);
   3462 		}
   3463 
   3464                 value->resolution.xres =
   3465 		    (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
   3466 		    buffer[3];
   3467                 value->resolution.yres =
   3468 		    (((((buffer[4] << 8) | buffer[5]) << 8) | buffer[6]) << 8) |
   3469 		    buffer[7];
   3470                 value->resolution.units =
   3471 		    (ipp_res_t)buffer[8];
   3472 	        break;
   3473 
   3474 	    case IPP_TAG_RANGE :
   3475 		if (n != 8)
   3476 		{
   3477 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3478 		                _("IPP rangeOfInteger value not 8 bytes."), 1);
   3479 		  DEBUG_printf(("1ippReadIO: bad rangeOfInteger value length "
   3480 		                "%d.", n));
   3481 		  _cupsBufferRelease((char *)buffer);
   3482 		  return (IPP_STATE_ERROR);
   3483 		}
   3484 
   3485 	        if ((*cb)(src, buffer, 8) < 8)
   3486 		{
   3487 	          DEBUG_puts("1ippReadIO: Unable to read range value.");
   3488 		  _cupsBufferRelease((char *)buffer);
   3489 		  return (IPP_STATE_ERROR);
   3490 		}
   3491 
   3492                 value->range.lower =
   3493 		    (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
   3494 		    buffer[3];
   3495                 value->range.upper =
   3496 		    (((((buffer[4] << 8) | buffer[5]) << 8) | buffer[6]) << 8) |
   3497 		    buffer[7];
   3498 	        break;
   3499 
   3500 	    case IPP_TAG_TEXTLANG :
   3501 	    case IPP_TAG_NAMELANG :
   3502 	        if (n < 4)
   3503 		{
   3504 		  if (tag == IPP_TAG_TEXTLANG)
   3505 		    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3506 		                  _("IPP textWithLanguage value less than "
   3507 		                    "minimum 4 bytes."), 1);
   3508 		  else
   3509 		    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3510 		                  _("IPP nameWithLanguage value less than "
   3511 		                    "minimum 4 bytes."), 1);
   3512 		  DEBUG_printf(("1ippReadIO: bad stringWithLanguage value "
   3513 		                "length %d.", n));
   3514 		  _cupsBufferRelease((char *)buffer);
   3515 		  return (IPP_STATE_ERROR);
   3516 		}
   3517 
   3518 	        if ((*cb)(src, buffer, (size_t)n) < n)
   3519 		{
   3520 	          DEBUG_puts("1ippReadIO: Unable to read string w/language "
   3521 		             "value.");
   3522 		  _cupsBufferRelease((char *)buffer);
   3523 		  return (IPP_STATE_ERROR);
   3524 		}
   3525 
   3526                 bufptr = buffer;
   3527 
   3528 	       /*
   3529 	        * text-with-language and name-with-language are composite
   3530 		* values:
   3531 		*
   3532 		*    language-length
   3533 		*    language
   3534 		*    text-length
   3535 		*    text
   3536 		*/
   3537 
   3538 		n = (bufptr[0] << 8) | bufptr[1];
   3539 
   3540 		if ((bufptr + 2 + n) >= (buffer + IPP_BUF_SIZE) || n >= (int)sizeof(string))
   3541 		{
   3542 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3543 		                _("IPP language length overflows value."), 1);
   3544 		  DEBUG_printf(("1ippReadIO: bad language value length %d.",
   3545 		                n));
   3546 		  _cupsBufferRelease((char *)buffer);
   3547 		  return (IPP_STATE_ERROR);
   3548 		}
   3549 		else if (n >= IPP_MAX_LANGUAGE)
   3550 		{
   3551 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3552 		                _("IPP language length too large."), 1);
   3553 		  DEBUG_printf(("1ippReadIO: bad language value length %d.",
   3554 		                n));
   3555 		  _cupsBufferRelease((char *)buffer);
   3556 		  return (IPP_STATE_ERROR);
   3557 		}
   3558 
   3559 		memcpy(string, bufptr + 2, (size_t)n);
   3560 		string[n] = '\0';
   3561 
   3562 		value->string.language = _cupsStrAlloc((char *)string);
   3563 
   3564                 bufptr += 2 + n;
   3565 		n = (bufptr[0] << 8) | bufptr[1];
   3566 
   3567 		if ((bufptr + 2 + n) >= (buffer + IPP_BUF_SIZE))
   3568 		{
   3569 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3570 		                _("IPP string length overflows value."), 1);
   3571 		  DEBUG_printf(("1ippReadIO: bad string value length %d.", n));
   3572 		  _cupsBufferRelease((char *)buffer);
   3573 		  return (IPP_STATE_ERROR);
   3574 		}
   3575 
   3576 		bufptr[2 + n] = '\0';
   3577                 value->string.text = _cupsStrAlloc((char *)bufptr + 2);
   3578 	        break;
   3579 
   3580             case IPP_TAG_BEGIN_COLLECTION :
   3581 	       /*
   3582 	        * Oh, boy, here comes a collection value, so read it...
   3583 		*/
   3584 
   3585                 value->collection = ippNew();
   3586 
   3587                 if (n > 0)
   3588 		{
   3589 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3590 		                _("IPP begCollection value not 0 bytes."), 1);
   3591 	          DEBUG_puts("1ippReadIO: begCollection tag with value length "
   3592 		             "> 0.");
   3593 		  _cupsBufferRelease((char *)buffer);
   3594 		  return (IPP_STATE_ERROR);
   3595 		}
   3596 
   3597 		if (ippReadIO(src, cb, 1, ipp, value->collection) == IPP_STATE_ERROR)
   3598 		{
   3599 	          DEBUG_puts("1ippReadIO: Unable to read collection value.");
   3600 		  _cupsBufferRelease((char *)buffer);
   3601 		  return (IPP_STATE_ERROR);
   3602 		}
   3603                 break;
   3604 
   3605             case IPP_TAG_END_COLLECTION :
   3606 		_cupsBufferRelease((char *)buffer);
   3607 
   3608                 if (n > 0)
   3609 		{
   3610 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3611 		                _("IPP endCollection value not 0 bytes."), 1);
   3612 	          DEBUG_puts("1ippReadIO: endCollection tag with value length "
   3613 		             "> 0.");
   3614 		  return (IPP_STATE_ERROR);
   3615 		}
   3616 
   3617 	        DEBUG_puts("1ippReadIO: endCollection tag...");
   3618 		return (ipp->state = IPP_STATE_DATA);
   3619 
   3620             case IPP_TAG_MEMBERNAME :
   3621 	       /*
   3622 	        * The value the name of the member in the collection, which
   3623 		* we need to carry over...
   3624 		*/
   3625 
   3626                 if (!attr)
   3627                 {
   3628 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3629 		                _("IPP memberName with no attribute."), 1);
   3630 	          DEBUG_puts("1ippReadIO: Member name without attribute.");
   3631 		  _cupsBufferRelease((char *)buffer);
   3632 		  return (IPP_STATE_ERROR);
   3633                 }
   3634 		else if (n == 0)
   3635 		{
   3636 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3637 		                _("IPP memberName value is empty."), 1);
   3638 	          DEBUG_puts("1ippReadIO: Empty member name value.");
   3639 		  _cupsBufferRelease((char *)buffer);
   3640 		  return (IPP_STATE_ERROR);
   3641 		}
   3642 		else if ((*cb)(src, buffer, (size_t)n) < n)
   3643 		{
   3644 	          DEBUG_puts("1ippReadIO: Unable to read member name value.");
   3645 		  _cupsBufferRelease((char *)buffer);
   3646 		  return (IPP_STATE_ERROR);
   3647 		}
   3648 
   3649 		buffer[n] = '\0';
   3650 		attr->name = _cupsStrAlloc((char *)buffer);
   3651 
   3652                /*
   3653 	        * Since collection members are encoded differently than
   3654 		* regular attributes, make sure we don't start with an
   3655 		* empty value...
   3656 		*/
   3657 
   3658                 attr->num_values --;
   3659 
   3660 		DEBUG_printf(("2ippReadIO: member name=\"%s\"", attr->name));
   3661 		break;
   3662 
   3663             default : /* Other unsupported values */
   3664                 if (tag == IPP_TAG_STRING && n > IPP_MAX_LENGTH)
   3665 		{
   3666 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3667 		                _("IPP octetString length too large."), 1);
   3668 		  DEBUG_printf(("1ippReadIO: bad octetString value length %d.",
   3669 		                n));
   3670 		  _cupsBufferRelease((char *)buffer);
   3671 		  return (IPP_STATE_ERROR);
   3672 		}
   3673 
   3674                 value->unknown.length = n;
   3675 
   3676 	        if (n > 0)
   3677 		{
   3678 		  if ((value->unknown.data = malloc((size_t)n)) == NULL)
   3679 		  {
   3680 		    _cupsSetHTTPError(HTTP_STATUS_ERROR);
   3681 		    DEBUG_puts("1ippReadIO: Unable to allocate value");
   3682 		    _cupsBufferRelease((char *)buffer);
   3683 		    return (IPP_STATE_ERROR);
   3684 		  }
   3685 
   3686 	          if ((*cb)(src, value->unknown.data, (size_t)n) < n)
   3687 		  {
   3688 	            DEBUG_puts("1ippReadIO: Unable to read unsupported value.");
   3689 		    _cupsBufferRelease((char *)buffer);
   3690 		    return (IPP_STATE_ERROR);
   3691 		  }
   3692 		}
   3693 		else
   3694 		  value->unknown.data = NULL;
   3695 	        break;
   3696 	  }
   3697 
   3698 	 /*
   3699           * If blocking is disabled, stop here...
   3700 	  */
   3701 
   3702           if (!blocking)
   3703 	    break;
   3704 	}
   3705         break;
   3706 
   3707     case IPP_STATE_DATA :
   3708         break;
   3709 
   3710     default :
   3711         break; /* anti-compiler-warning-code */
   3712   }
   3713 
   3714   DEBUG_printf(("1ippReadIO: returning ipp->state=%d.", ipp->state));
   3715   _cupsBufferRelease((char *)buffer);
   3716 
   3717   return (ipp->state);
   3718 }
   3719 
   3720 
   3721 /*
   3722  * 'ippSetBoolean()' - Set a boolean value in an attribute.
   3723  *
   3724  * The @code ipp@ parameter refers to an IPP message previously created using
   3725  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   3726  *
   3727  * The @code attr@ parameter may be modified as a result of setting the value.
   3728  *
   3729  * The @code element@ parameter specifies which value to set from 0 to
   3730  * @code ippGetCount(attr)@.
   3731  *
   3732  * @since CUPS 1.6/macOS 10.8@
   3733  */
   3734 
   3735 int					/* O  - 1 on success, 0 on failure */
   3736 ippSetBoolean(ipp_t           *ipp,	/* I  - IPP message */
   3737               ipp_attribute_t **attr,	/* IO - IPP attribute */
   3738               int             element,	/* I  - Value number (0-based) */
   3739               int             boolvalue)/* I  - Boolean value */
   3740 {
   3741   _ipp_value_t	*value;			/* Current value */
   3742 
   3743 
   3744  /*
   3745   * Range check input...
   3746   */
   3747 
   3748   if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BOOLEAN ||
   3749       element < 0 || element > (*attr)->num_values)
   3750     return (0);
   3751 
   3752  /*
   3753   * Set the value and return...
   3754   */
   3755 
   3756   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   3757     value->boolean = (char)boolvalue;
   3758 
   3759   return (value != NULL);
   3760 }
   3761 
   3762 
   3763 /*
   3764  * 'ippSetCollection()' - Set a collection value in an attribute.
   3765  *
   3766  * The @code ipp@ parameter refers to an IPP message previously created using
   3767  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   3768  *
   3769  * The @code attr@ parameter may be modified as a result of setting the value.
   3770  *
   3771  * The @code element@ parameter specifies which value to set from 0 to
   3772  * @code ippGetCount(attr)@.
   3773  *
   3774  * @since CUPS 1.6/macOS 10.8@
   3775  */
   3776 
   3777 int					/* O  - 1 on success, 0 on failure */
   3778 ippSetCollection(
   3779     ipp_t           *ipp,		/* I  - IPP message */
   3780     ipp_attribute_t **attr,		/* IO - IPP attribute */
   3781     int             element,		/* I  - Value number (0-based) */
   3782     ipp_t           *colvalue)		/* I  - Collection value */
   3783 {
   3784   _ipp_value_t	*value;			/* Current value */
   3785 
   3786 
   3787  /*
   3788   * Range check input...
   3789   */
   3790 
   3791   if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BEGIN_COLLECTION ||
   3792       element < 0 || element > (*attr)->num_values || !colvalue)
   3793     return (0);
   3794 
   3795  /*
   3796   * Set the value and return...
   3797   */
   3798 
   3799   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   3800   {
   3801     if (value->collection)
   3802       ippDelete(value->collection);
   3803 
   3804     value->collection = colvalue;
   3805     colvalue->use ++;
   3806   }
   3807 
   3808   return (value != NULL);
   3809 }
   3810 
   3811 
   3812 /*
   3813  * 'ippSetDate()' - Set a dateTime value in an attribute.
   3814  *
   3815  * The @code ipp@ parameter refers to an IPP message previously created using
   3816  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   3817  *
   3818  * The @code attr@ parameter may be modified as a result of setting the value.
   3819  *
   3820  * The @code element@ parameter specifies which value to set from 0 to
   3821  * @code ippGetCount(attr)@.
   3822  *
   3823  * @since CUPS 1.6/macOS 10.8@
   3824  */
   3825 
   3826 int					/* O  - 1 on success, 0 on failure */
   3827 ippSetDate(ipp_t             *ipp,	/* I  - IPP message */
   3828            ipp_attribute_t   **attr,	/* IO - IPP attribute */
   3829            int               element,	/* I  - Value number (0-based) */
   3830            const ipp_uchar_t *datevalue)/* I  - dateTime value */
   3831 {
   3832   _ipp_value_t	*value;			/* Current value */
   3833 
   3834 
   3835  /*
   3836   * Range check input...
   3837   */
   3838 
   3839   if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_DATE ||
   3840       element < 0 || element > (*attr)->num_values || !datevalue)
   3841     return (0);
   3842 
   3843  /*
   3844   * Set the value and return...
   3845   */
   3846 
   3847   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   3848     memcpy(value->date, datevalue, sizeof(value->date));
   3849 
   3850   return (value != NULL);
   3851 }
   3852 
   3853 
   3854 /*
   3855  * 'ippSetGroupTag()' - Set the group tag of an attribute.
   3856  *
   3857  * The @code ipp@ parameter refers to an IPP message previously created using
   3858  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   3859  *
   3860  * The @code attr@ parameter may be modified as a result of setting the value.
   3861  *
   3862  * The @code group@ parameter specifies the IPP attribute group tag: none
   3863  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
   3864  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
   3865  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
   3866  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
   3867  *
   3868  * @since CUPS 1.6/macOS 10.8@
   3869  */
   3870 
   3871 int					/* O  - 1 on success, 0 on failure */
   3872 ippSetGroupTag(
   3873     ipp_t           *ipp,		/* I  - IPP message */
   3874     ipp_attribute_t **attr,		/* IO - Attribute */
   3875     ipp_tag_t       group_tag)		/* I  - Group tag */
   3876 {
   3877  /*
   3878   * Range check input - group tag must be 0x01 to 0x0F, per RFC 8011...
   3879   */
   3880 
   3881   if (!ipp || !attr || !*attr ||
   3882       group_tag < IPP_TAG_ZERO || group_tag == IPP_TAG_END ||
   3883       group_tag >= IPP_TAG_UNSUPPORTED_VALUE)
   3884     return (0);
   3885 
   3886  /*
   3887   * Set the group tag and return...
   3888   */
   3889 
   3890   (*attr)->group_tag = group_tag;
   3891 
   3892   return (1);
   3893 }
   3894 
   3895 
   3896 /*
   3897  * 'ippSetInteger()' - Set an integer or enum value in an attribute.
   3898  *
   3899  * The @code ipp@ parameter refers to an IPP message previously created using
   3900  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   3901  *
   3902  * The @code attr@ parameter may be modified as a result of setting the value.
   3903  *
   3904  * The @code element@ parameter specifies which value to set from 0 to
   3905  * @code ippGetCount(attr)@.
   3906  *
   3907  * @since CUPS 1.6/macOS 10.8@
   3908  */
   3909 
   3910 int					/* O  - 1 on success, 0 on failure */
   3911 ippSetInteger(ipp_t           *ipp,	/* I  - IPP message */
   3912               ipp_attribute_t **attr,	/* IO - IPP attribute */
   3913               int             element,	/* I  - Value number (0-based) */
   3914               int             intvalue)	/* I  - Integer/enum value */
   3915 {
   3916   _ipp_value_t	*value;			/* Current value */
   3917 
   3918 
   3919  /*
   3920   * Range check input...
   3921   */
   3922 
   3923   if (!ipp || !attr || !*attr ||
   3924       ((*attr)->value_tag != IPP_TAG_INTEGER && (*attr)->value_tag != IPP_TAG_ENUM) ||
   3925       element < 0 || element > (*attr)->num_values)
   3926     return (0);
   3927 
   3928  /*
   3929   * Set the value and return...
   3930   */
   3931 
   3932   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   3933     value->integer = intvalue;
   3934 
   3935   return (value != NULL);
   3936 }
   3937 
   3938 
   3939 /*
   3940  * 'ippSetName()' - Set the name of an attribute.
   3941  *
   3942  * The @code ipp@ parameter refers to an IPP message previously created using
   3943  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   3944  *
   3945  * The @code attr@ parameter may be modified as a result of setting the value.
   3946  *
   3947  * @since CUPS 1.6/macOS 10.8@
   3948  */
   3949 
   3950 int					/* O  - 1 on success, 0 on failure */
   3951 ippSetName(ipp_t           *ipp,	/* I  - IPP message */
   3952 	   ipp_attribute_t **attr,	/* IO - IPP attribute */
   3953 	   const char      *name)	/* I  - Attribute name */
   3954 {
   3955   char	*temp;				/* Temporary name value */
   3956 
   3957 
   3958  /*
   3959   * Range check input...
   3960   */
   3961 
   3962   if (!ipp || !attr || !*attr)
   3963     return (0);
   3964 
   3965  /*
   3966   * Set the value and return...
   3967   */
   3968 
   3969   if ((temp = _cupsStrAlloc(name)) != NULL)
   3970   {
   3971     if ((*attr)->name)
   3972       _cupsStrFree((*attr)->name);
   3973 
   3974     (*attr)->name = temp;
   3975   }
   3976 
   3977   return (temp != NULL);
   3978 }
   3979 
   3980 
   3981 /*
   3982  * 'ippSetOctetString()' - Set an octetString value in an IPP attribute.
   3983  *
   3984  * The @code ipp@ parameter refers to an IPP message previously created using
   3985  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   3986  *
   3987  * The @code attr@ parameter may be modified as a result of setting the value.
   3988  *
   3989  * The @code element@ parameter specifies which value to set from 0 to
   3990  * @code ippGetCount(attr)@.
   3991  *
   3992  * @since CUPS 1.7/macOS 10.9@
   3993  */
   3994 
   3995 int					/* O  - 1 on success, 0 on failure */
   3996 ippSetOctetString(
   3997     ipp_t           *ipp,		/* I  - IPP message */
   3998     ipp_attribute_t **attr,		/* IO - IPP attribute */
   3999     int             element,		/* I  - Value number (0-based) */
   4000     const void      *data,		/* I  - Pointer to octetString data */
   4001     int             datalen)		/* I  - Length of octetString data */
   4002 {
   4003   _ipp_value_t	*value;			/* Current value */
   4004 
   4005 
   4006  /*
   4007   * Range check input...
   4008   */
   4009 
   4010   if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_STRING ||
   4011       element < 0 || element > (*attr)->num_values ||
   4012       datalen < 0 || datalen > IPP_MAX_LENGTH)
   4013     return (0);
   4014 
   4015  /*
   4016   * Set the value and return...
   4017   */
   4018 
   4019   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   4020   {
   4021     if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST)
   4022     {
   4023      /*
   4024       * Just copy the pointer...
   4025       */
   4026 
   4027       value->unknown.data   = (void *)data;
   4028       value->unknown.length = datalen;
   4029     }
   4030     else
   4031     {
   4032      /*
   4033       * Copy the data...
   4034       */
   4035 
   4036       if (value->unknown.data)
   4037       {
   4038        /*
   4039 	* Free previous data...
   4040 	*/
   4041 
   4042 	free(value->unknown.data);
   4043 
   4044 	value->unknown.data   = NULL;
   4045         value->unknown.length = 0;
   4046       }
   4047 
   4048       if (datalen > 0)
   4049       {
   4050 	void	*temp;			/* Temporary data pointer */
   4051 
   4052 	if ((temp = malloc((size_t)datalen)) != NULL)
   4053 	{
   4054 	  memcpy(temp, data, (size_t)datalen);
   4055 
   4056 	  value->unknown.data   = temp;
   4057 	  value->unknown.length = datalen;
   4058 	}
   4059 	else
   4060 	  return (0);
   4061       }
   4062     }
   4063   }
   4064 
   4065   return (value != NULL);
   4066 }
   4067 
   4068 
   4069 /*
   4070  * 'ippSetOperation()' - Set the operation ID in an IPP request message.
   4071  *
   4072  * The @code ipp@ parameter refers to an IPP message previously created using
   4073  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4074  *
   4075  * @since CUPS 1.6/macOS 10.8@
   4076  */
   4077 
   4078 int					/* O - 1 on success, 0 on failure */
   4079 ippSetOperation(ipp_t    *ipp,		/* I - IPP request message */
   4080                 ipp_op_t op)		/* I - Operation ID */
   4081 {
   4082  /*
   4083   * Range check input...
   4084   */
   4085 
   4086   if (!ipp)
   4087     return (0);
   4088 
   4089  /*
   4090   * Set the operation and return...
   4091   */
   4092 
   4093   ipp->request.op.operation_id = op;
   4094 
   4095   return (1);
   4096 }
   4097 
   4098 
   4099 /*
   4100  * 'ippSetRange()' - Set a rangeOfInteger value in an attribute.
   4101  *
   4102  * The @code ipp@ parameter refers to an IPP message previously created using
   4103  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4104  *
   4105  * The @code attr@ parameter may be modified as a result of setting the value.
   4106  *
   4107  * The @code element@ parameter specifies which value to set from 0 to
   4108  * @code ippGetCount(attr)@.
   4109  *
   4110  * @since CUPS 1.6/macOS 10.8@
   4111  */
   4112 
   4113 int					/* O  - 1 on success, 0 on failure */
   4114 ippSetRange(ipp_t           *ipp,	/* I  - IPP message */
   4115             ipp_attribute_t **attr,	/* IO - IPP attribute */
   4116             int             element,	/* I  - Value number (0-based) */
   4117 	    int             lowervalue,	/* I  - Lower bound for range */
   4118 	    int             uppervalue)	/* I  - Upper bound for range */
   4119 {
   4120   _ipp_value_t	*value;			/* Current value */
   4121 
   4122 
   4123  /*
   4124   * Range check input...
   4125   */
   4126 
   4127   if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_RANGE ||
   4128       element < 0 || element > (*attr)->num_values || lowervalue > uppervalue)
   4129     return (0);
   4130 
   4131  /*
   4132   * Set the value and return...
   4133   */
   4134 
   4135   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   4136   {
   4137     value->range.lower = lowervalue;
   4138     value->range.upper = uppervalue;
   4139   }
   4140 
   4141   return (value != NULL);
   4142 }
   4143 
   4144 
   4145 /*
   4146  * 'ippSetRequestId()' - Set the request ID in an IPP message.
   4147  *
   4148  * The @code ipp@ parameter refers to an IPP message previously created using
   4149  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4150  *
   4151  * The @code request_id@ parameter must be greater than 0.
   4152  *
   4153  * @since CUPS 1.6/macOS 10.8@
   4154  */
   4155 
   4156 int					/* O - 1 on success, 0 on failure */
   4157 ippSetRequestId(ipp_t *ipp,		/* I - IPP message */
   4158                 int   request_id)	/* I - Request ID */
   4159 {
   4160  /*
   4161   * Range check input; not checking request_id values since ipptool wants to send
   4162   * invalid values for conformance testing and a bad request_id does not affect the
   4163   * encoding of a message...
   4164   */
   4165 
   4166   if (!ipp)
   4167     return (0);
   4168 
   4169  /*
   4170   * Set the request ID and return...
   4171   */
   4172 
   4173   ipp->request.any.request_id = request_id;
   4174 
   4175   return (1);
   4176 }
   4177 
   4178 
   4179 /*
   4180  * 'ippSetResolution()' - Set a resolution value in an attribute.
   4181  *
   4182  * The @code ipp@ parameter refers to an IPP message previously created using
   4183  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4184  *
   4185  * The @code attr@ parameter may be modified as a result of setting the value.
   4186  *
   4187  * The @code element@ parameter specifies which value to set from 0 to
   4188  * @code ippGetCount(attr)@.
   4189  *
   4190  * @since CUPS 1.6/macOS 10.8@
   4191  */
   4192 
   4193 int					/* O  - 1 on success, 0 on failure */
   4194 ippSetResolution(
   4195     ipp_t           *ipp,		/* I  - IPP message */
   4196     ipp_attribute_t **attr,		/* IO - IPP attribute */
   4197     int             element,		/* I  - Value number (0-based) */
   4198     ipp_res_t       unitsvalue,		/* I  - Resolution units */
   4199     int             xresvalue,		/* I  - Horizontal/cross feed resolution */
   4200     int             yresvalue)		/* I  - Vertical/feed resolution */
   4201 {
   4202   _ipp_value_t	*value;			/* Current value */
   4203 
   4204 
   4205  /*
   4206   * Range check input...
   4207   */
   4208 
   4209   if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_RESOLUTION ||
   4210       element < 0 || element > (*attr)->num_values || xresvalue <= 0 || yresvalue <= 0 ||
   4211       unitsvalue < IPP_RES_PER_INCH || unitsvalue > IPP_RES_PER_CM)
   4212     return (0);
   4213 
   4214  /*
   4215   * Set the value and return...
   4216   */
   4217 
   4218   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   4219   {
   4220     value->resolution.units = unitsvalue;
   4221     value->resolution.xres  = xresvalue;
   4222     value->resolution.yres  = yresvalue;
   4223   }
   4224 
   4225   return (value != NULL);
   4226 }
   4227 
   4228 
   4229 /*
   4230  * 'ippSetState()' - Set the current state of the IPP message.
   4231  *
   4232  * @since CUPS 1.6/macOS 10.8@
   4233  */
   4234 
   4235 int					/* O - 1 on success, 0 on failure */
   4236 ippSetState(ipp_t       *ipp,		/* I - IPP message */
   4237             ipp_state_t state)		/* I - IPP state value */
   4238 {
   4239  /*
   4240   * Range check input...
   4241   */
   4242 
   4243   if (!ipp)
   4244     return (0);
   4245 
   4246  /*
   4247   * Set the state and return...
   4248   */
   4249 
   4250   ipp->state   = state;
   4251   ipp->current = NULL;
   4252 
   4253   return (1);
   4254 }
   4255 
   4256 
   4257 /*
   4258  * 'ippSetStatusCode()' - Set the status code in an IPP response or event message.
   4259  *
   4260  * The @code ipp@ parameter refers to an IPP message previously created using
   4261  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4262  *
   4263  * @since CUPS 1.6/macOS 10.8@
   4264  */
   4265 
   4266 int					/* O - 1 on success, 0 on failure */
   4267 ippSetStatusCode(ipp_t        *ipp,	/* I - IPP response or event message */
   4268                  ipp_status_t status)	/* I - Status code */
   4269 {
   4270  /*
   4271   * Range check input...
   4272   */
   4273 
   4274   if (!ipp)
   4275     return (0);
   4276 
   4277  /*
   4278   * Set the status code and return...
   4279   */
   4280 
   4281   ipp->request.status.status_code = status;
   4282 
   4283   return (1);
   4284 }
   4285 
   4286 
   4287 /*
   4288  * 'ippSetString()' - Set a string value in an attribute.
   4289  *
   4290  * The @code ipp@ parameter refers to an IPP message previously created using
   4291  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4292  *
   4293  * The @code attr@ parameter may be modified as a result of setting the value.
   4294  *
   4295  * The @code element@ parameter specifies which value to set from 0 to
   4296  * @code ippGetCount(attr)@.
   4297  *
   4298  * @since CUPS 1.6/macOS 10.8@
   4299  */
   4300 
   4301 int					/* O  - 1 on success, 0 on failure */
   4302 ippSetString(ipp_t           *ipp,	/* I  - IPP message */
   4303              ipp_attribute_t **attr,	/* IO - IPP attribute */
   4304              int             element,	/* I  - Value number (0-based) */
   4305 	     const char      *strvalue)	/* I  - String value */
   4306 {
   4307   char		*temp;			/* Temporary string */
   4308   _ipp_value_t	*value;			/* Current value */
   4309 
   4310 
   4311  /*
   4312   * Range check input...
   4313   */
   4314 
   4315   if (!ipp || !attr || !*attr ||
   4316       ((*attr)->value_tag != IPP_TAG_TEXTLANG &&
   4317       (*attr)->value_tag != IPP_TAG_NAMELANG &&
   4318        ((*attr)->value_tag < IPP_TAG_TEXT ||
   4319         (*attr)->value_tag > IPP_TAG_MIMETYPE)) ||
   4320       element < 0 || element > (*attr)->num_values || !strvalue)
   4321     return (0);
   4322 
   4323  /*
   4324   * Set the value and return...
   4325   */
   4326 
   4327   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   4328   {
   4329     if (element > 0)
   4330       value->string.language = (*attr)->values[0].string.language;
   4331 
   4332     if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST)
   4333       value->string.text = (char *)strvalue;
   4334     else if ((temp = _cupsStrAlloc(strvalue)) != NULL)
   4335     {
   4336       if (value->string.text)
   4337         _cupsStrFree(value->string.text);
   4338 
   4339       value->string.text = temp;
   4340     }
   4341     else
   4342       return (0);
   4343   }
   4344 
   4345   return (value != NULL);
   4346 }
   4347 
   4348 
   4349 /*
   4350  * 'ippSetStringf()' - Set a formatted string value of an attribute.
   4351  *
   4352  * The @code ipp@ parameter refers to an IPP message previously created using
   4353  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4354  *
   4355  * The @code attr@ parameter may be modified as a result of setting the value.
   4356  *
   4357  * The @code element@ parameter specifies which value to set from 0 to
   4358  * @code ippGetCount(attr)@.
   4359  *
   4360  * The @code format@ parameter uses formatting characters compatible with the
   4361  * printf family of standard functions.  Additional arguments follow it as
   4362  * needed.  The formatted string is truncated as needed to the maximum length of
   4363  * the corresponding value type.
   4364  *
   4365  * @since CUPS 1.7/macOS 10.9@
   4366  */
   4367 
   4368 int					/* O  - 1 on success, 0 on failure */
   4369 ippSetStringf(ipp_t           *ipp,	/* I  - IPP message */
   4370               ipp_attribute_t **attr,	/* IO - IPP attribute */
   4371               int             element,	/* I  - Value number (0-based) */
   4372 	      const char      *format,	/* I  - Printf-style format string */
   4373 	      ...)			/* I  - Additional arguments as needed */
   4374 {
   4375   int		ret;			/* Return value */
   4376   va_list	ap;			/* Pointer to additional arguments */
   4377 
   4378 
   4379   va_start(ap, format);
   4380   ret = ippSetStringfv(ipp, attr, element, format, ap);
   4381   va_end(ap);
   4382 
   4383   return (ret);
   4384 }
   4385 
   4386 
   4387 /*
   4388  * 'ippSetStringf()' - Set a formatted string value of an attribute.
   4389  *
   4390  * The @code ipp@ parameter refers to an IPP message previously created using
   4391  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4392  *
   4393  * The @code attr@ parameter may be modified as a result of setting the value.
   4394  *
   4395  * The @code element@ parameter specifies which value to set from 0 to
   4396  * @code ippGetCount(attr)@.
   4397  *
   4398  * The @code format@ parameter uses formatting characters compatible with the
   4399  * printf family of standard functions.  Additional arguments follow it as
   4400  * needed.  The formatted string is truncated as needed to the maximum length of
   4401  * the corresponding value type.
   4402  *
   4403  * @since CUPS 1.7/macOS 10.9@
   4404  */
   4405 
   4406 int					/* O  - 1 on success, 0 on failure */
   4407 ippSetStringfv(ipp_t           *ipp,	/* I  - IPP message */
   4408                ipp_attribute_t **attr,	/* IO - IPP attribute */
   4409                int             element,	/* I  - Value number (0-based) */
   4410 	       const char      *format,	/* I  - Printf-style format string */
   4411 	       va_list         ap)	/* I  - Pointer to additional arguments */
   4412 {
   4413   ipp_tag_t	value_tag;		/* Value tag */
   4414   char		buffer[IPP_MAX_TEXT + 4];
   4415 					/* Formatted text string */
   4416   ssize_t	bytes,			/* Length of formatted value */
   4417 		max_bytes;		/* Maximum number of bytes for value */
   4418 
   4419 
   4420  /*
   4421   * Range check input...
   4422   */
   4423 
   4424   if (attr && *attr)
   4425     value_tag = (*attr)->value_tag & IPP_TAG_CUPS_MASK;
   4426   else
   4427     value_tag = IPP_TAG_ZERO;
   4428 
   4429   if (!ipp || !attr || !*attr ||
   4430       (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG &&
   4431        value_tag != IPP_TAG_NAMELANG) || value_tag > IPP_TAG_MIMETYPE ||
   4432       !format)
   4433     return (0);
   4434 
   4435  /*
   4436   * Format the string...
   4437   */
   4438 
   4439   if (!strcmp(format, "%s"))
   4440   {
   4441    /*
   4442     * Optimize the simple case...
   4443     */
   4444 
   4445     const char *s = va_arg(ap, char *);
   4446 
   4447     if (!s)
   4448       s = "(null)";
   4449 
   4450     bytes = (ssize_t)strlen(s);
   4451     strlcpy(buffer, s, sizeof(buffer));
   4452   }
   4453   else
   4454   {
   4455    /*
   4456     * Do a full formatting of the message...
   4457     */
   4458 
   4459     if ((bytes = vsnprintf(buffer, sizeof(buffer), format, ap)) < 0)
   4460       return (0);
   4461   }
   4462 
   4463  /*
   4464   * Limit the length of the string...
   4465   */
   4466 
   4467   switch (value_tag)
   4468   {
   4469     default :
   4470     case IPP_TAG_TEXT :
   4471     case IPP_TAG_TEXTLANG :
   4472         max_bytes = IPP_MAX_TEXT;
   4473         break;
   4474 
   4475     case IPP_TAG_NAME :
   4476     case IPP_TAG_NAMELANG :
   4477         max_bytes = IPP_MAX_NAME;
   4478         break;
   4479 
   4480     case IPP_TAG_CHARSET :
   4481         max_bytes = IPP_MAX_CHARSET;
   4482         break;
   4483 
   4484     case IPP_TAG_KEYWORD :
   4485         max_bytes = IPP_MAX_KEYWORD;
   4486         break;
   4487 
   4488     case IPP_TAG_LANGUAGE :
   4489         max_bytes = IPP_MAX_LANGUAGE;
   4490         break;
   4491 
   4492     case IPP_TAG_MIMETYPE :
   4493         max_bytes = IPP_MAX_MIMETYPE;
   4494         break;
   4495 
   4496     case IPP_TAG_URI :
   4497         max_bytes = IPP_MAX_URI;
   4498         break;
   4499 
   4500     case IPP_TAG_URISCHEME :
   4501         max_bytes = IPP_MAX_URISCHEME;
   4502         break;
   4503   }
   4504 
   4505   if (bytes >= max_bytes)
   4506   {
   4507     char	*bufmax,		/* Buffer at max_bytes */
   4508 		*bufptr;		/* Pointer into buffer */
   4509 
   4510     bufptr = buffer + strlen(buffer) - 1;
   4511     bufmax = buffer + max_bytes - 1;
   4512 
   4513     while (bufptr > bufmax)
   4514     {
   4515       if (*bufptr & 0x80)
   4516       {
   4517         while ((*bufptr & 0xc0) == 0x80 && bufptr > buffer)
   4518           bufptr --;
   4519       }
   4520 
   4521       bufptr --;
   4522     }
   4523 
   4524     *bufptr = '\0';
   4525   }
   4526 
   4527  /*
   4528   * Set the formatted string and return...
   4529   */
   4530 
   4531   return (ippSetString(ipp, attr, element, buffer));
   4532 }
   4533 
   4534 
   4535 /*
   4536  * 'ippSetValueTag()' - Set the value tag of an attribute.
   4537  *
   4538  * The @code ipp@ parameter refers to an IPP message previously created using
   4539  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4540  *
   4541  * The @code attr@ parameter may be modified as a result of setting the value.
   4542  *
   4543  * Integer (@code IPP_TAG_INTEGER@) values can be promoted to rangeOfInteger
   4544  * (@code IPP_TAG_RANGE@) values, the various string tags can be promoted to name
   4545  * (@code IPP_TAG_NAME@) or nameWithLanguage (@code IPP_TAG_NAMELANG@) values, text
   4546  * (@code IPP_TAG_TEXT@) values can be promoted to textWithLanguage
   4547  * (@code IPP_TAG_TEXTLANG@) values, and all values can be demoted to the various
   4548  * out-of-band value tags such as no-value (@code IPP_TAG_NOVALUE@). All other changes
   4549  * will be rejected.
   4550  *
   4551  * Promoting a string attribute to nameWithLanguage or textWithLanguage adds the language
   4552  * code in the "attributes-natural-language" attribute or, if not present, the language
   4553  * code for the current locale.
   4554  *
   4555  * @since CUPS 1.6/macOS 10.8@
   4556  */
   4557 
   4558 int					/* O  - 1 on success, 0 on failure */
   4559 ippSetValueTag(
   4560     ipp_t          *ipp,		/* I  - IPP message */
   4561     ipp_attribute_t **attr,		/* IO - IPP attribute */
   4562     ipp_tag_t       value_tag)		/* I  - Value tag */
   4563 {
   4564   int		i;			/* Looping var */
   4565   _ipp_value_t	*value;			/* Current value */
   4566   int		integer;		/* Current integer value */
   4567   cups_lang_t	*language;		/* Current language */
   4568   char		code[32];		/* Language code */
   4569   ipp_tag_t	temp_tag;		/* Temporary value tag */
   4570 
   4571 
   4572  /*
   4573   * Range check input...
   4574   */
   4575 
   4576   if (!ipp || !attr || !*attr)
   4577     return (0);
   4578 
   4579  /*
   4580   * If there is no change, return immediately...
   4581   */
   4582 
   4583   if (value_tag == (*attr)->value_tag)
   4584     return (1);
   4585 
   4586  /*
   4587   * Otherwise implement changes as needed...
   4588   */
   4589 
   4590   temp_tag = (ipp_tag_t)((int)((*attr)->value_tag) & IPP_TAG_CUPS_MASK);
   4591 
   4592   switch (value_tag)
   4593   {
   4594     case IPP_TAG_UNSUPPORTED_VALUE :
   4595     case IPP_TAG_DEFAULT :
   4596     case IPP_TAG_UNKNOWN :
   4597     case IPP_TAG_NOVALUE :
   4598     case IPP_TAG_NOTSETTABLE :
   4599     case IPP_TAG_DELETEATTR :
   4600     case IPP_TAG_ADMINDEFINE :
   4601        /*
   4602         * Free any existing values...
   4603         */
   4604 
   4605         if ((*attr)->num_values > 0)
   4606           ipp_free_values(*attr, 0, (*attr)->num_values);
   4607 
   4608        /*
   4609         * Set out-of-band value...
   4610         */
   4611 
   4612         (*attr)->value_tag = value_tag;
   4613         break;
   4614 
   4615     case IPP_TAG_RANGE :
   4616         if (temp_tag != IPP_TAG_INTEGER)
   4617           return (0);
   4618 
   4619         for (i = (*attr)->num_values, value = (*attr)->values;
   4620              i > 0;
   4621              i --, value ++)
   4622         {
   4623           integer            = value->integer;
   4624           value->range.lower = value->range.upper = integer;
   4625         }
   4626 
   4627         (*attr)->value_tag = IPP_TAG_RANGE;
   4628         break;
   4629 
   4630     case IPP_TAG_NAME :
   4631         if (temp_tag != IPP_TAG_KEYWORD && temp_tag != IPP_TAG_URI &&
   4632             temp_tag != IPP_TAG_URISCHEME && temp_tag != IPP_TAG_LANGUAGE &&
   4633             temp_tag != IPP_TAG_MIMETYPE)
   4634           return (0);
   4635 
   4636         (*attr)->value_tag = (ipp_tag_t)(IPP_TAG_NAME | ((*attr)->value_tag & IPP_TAG_CUPS_CONST));
   4637         break;
   4638 
   4639     case IPP_TAG_NAMELANG :
   4640     case IPP_TAG_TEXTLANG :
   4641         if (value_tag == IPP_TAG_NAMELANG &&
   4642             (temp_tag != IPP_TAG_NAME && temp_tag != IPP_TAG_KEYWORD &&
   4643              temp_tag != IPP_TAG_URI && temp_tag != IPP_TAG_URISCHEME &&
   4644              temp_tag != IPP_TAG_LANGUAGE && temp_tag != IPP_TAG_MIMETYPE))
   4645           return (0);
   4646 
   4647         if (value_tag == IPP_TAG_TEXTLANG && temp_tag != IPP_TAG_TEXT)
   4648           return (0);
   4649 
   4650         if (ipp->attrs && ipp->attrs->next && ipp->attrs->next->name &&
   4651             !strcmp(ipp->attrs->next->name, "attributes-natural-language"))
   4652         {
   4653          /*
   4654           * Use the language code from the IPP message...
   4655           */
   4656 
   4657 	  (*attr)->values[0].string.language =
   4658 	      _cupsStrAlloc(ipp->attrs->next->values[0].string.text);
   4659         }
   4660         else
   4661         {
   4662          /*
   4663           * Otherwise, use the language code corresponding to the locale...
   4664           */
   4665 
   4666 	  language = cupsLangDefault();
   4667 	  (*attr)->values[0].string.language = _cupsStrAlloc(ipp_lang_code(language->language,
   4668 									code,
   4669 									sizeof(code)));
   4670         }
   4671 
   4672         for (i = (*attr)->num_values - 1, value = (*attr)->values + 1;
   4673              i > 0;
   4674              i --, value ++)
   4675           value->string.language = (*attr)->values[0].string.language;
   4676 
   4677         if ((int)(*attr)->value_tag & IPP_TAG_CUPS_CONST)
   4678         {
   4679          /*
   4680           * Make copies of all values...
   4681           */
   4682 
   4683 	  for (i = (*attr)->num_values, value = (*attr)->values;
   4684 	       i > 0;
   4685 	       i --, value ++)
   4686 	    value->string.text = _cupsStrAlloc(value->string.text);
   4687         }
   4688 
   4689         (*attr)->value_tag = IPP_TAG_NAMELANG;
   4690         break;
   4691 
   4692     case IPP_TAG_KEYWORD :
   4693         if (temp_tag == IPP_TAG_NAME || temp_tag == IPP_TAG_NAMELANG)
   4694           break;			/* Silently "allow" name -> keyword */
   4695 
   4696     default :
   4697         return (0);
   4698   }
   4699 
   4700   return (1);
   4701 }
   4702 
   4703 
   4704 /*
   4705  * 'ippSetVersion()' - Set the version number in an IPP message.
   4706  *
   4707  * The @code ipp@ parameter refers to an IPP message previously created using
   4708  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4709  *
   4710  * The valid version numbers are currently 1.0, 1.1, 2.0, 2.1, and 2.2.
   4711  *
   4712  * @since CUPS 1.6/macOS 10.8@
   4713  */
   4714 
   4715 int					/* O - 1 on success, 0 on failure */
   4716 ippSetVersion(ipp_t *ipp,		/* I - IPP message */
   4717               int   major,		/* I - Major version number (major.minor) */
   4718               int   minor)		/* I - Minor version number (major.minor) */
   4719 {
   4720  /*
   4721   * Range check input...
   4722   */
   4723 
   4724   if (!ipp || major < 0 || minor < 0)
   4725     return (0);
   4726 
   4727  /*
   4728   * Set the version number...
   4729   */
   4730 
   4731   ipp->request.any.version[0] = (ipp_uchar_t)major;
   4732   ipp->request.any.version[1] = (ipp_uchar_t)minor;
   4733 
   4734   return (1);
   4735 }
   4736 
   4737 
   4738 /*
   4739  * 'ippTimeToDate()' - Convert from time in seconds to RFC 2579 format.
   4740  */
   4741 
   4742 const ipp_uchar_t *			/* O - RFC-2579 date/time data */
   4743 ippTimeToDate(time_t t)			/* I - Time in seconds */
   4744 {
   4745   struct tm	*unixdate;		/* UNIX unixdate/time info */
   4746   ipp_uchar_t	*date = _cupsGlobals()->ipp_date;
   4747 					/* RFC-2579 date/time data */
   4748 
   4749 
   4750  /*
   4751   * RFC-2579 date/time format is:
   4752   *
   4753   *    Byte(s)  Description
   4754   *    -------  -----------
   4755   *    0-1      Year (0 to 65535)
   4756   *    2        Month (1 to 12)
   4757   *    3        Day (1 to 31)
   4758   *    4        Hours (0 to 23)
   4759   *    5        Minutes (0 to 59)
   4760   *    6        Seconds (0 to 60, 60 = "leap second")
   4761   *    7        Deciseconds (0 to 9)
   4762   *    8        +/- UTC
   4763   *    9        UTC hours (0 to 11)
   4764   *    10       UTC minutes (0 to 59)
   4765   */
   4766 
   4767   unixdate = gmtime(&t);
   4768   unixdate->tm_year += 1900;
   4769 
   4770   date[0]  = (ipp_uchar_t)(unixdate->tm_year >> 8);
   4771   date[1]  = (ipp_uchar_t)(unixdate->tm_year);
   4772   date[2]  = (ipp_uchar_t)(unixdate->tm_mon + 1);
   4773   date[3]  = (ipp_uchar_t)unixdate->tm_mday;
   4774   date[4]  = (ipp_uchar_t)unixdate->tm_hour;
   4775   date[5]  = (ipp_uchar_t)unixdate->tm_min;
   4776   date[6]  = (ipp_uchar_t)unixdate->tm_sec;
   4777   date[7]  = 0;
   4778   date[8]  = '+';
   4779   date[9]  = 0;
   4780   date[10] = 0;
   4781 
   4782   return (date);
   4783 }
   4784 
   4785 
   4786 /*
   4787  * 'ippValidateAttribute()' - Validate the contents of an attribute.
   4788  *
   4789  * This function validates the contents of an attribute based on the name and
   4790  * value tag.  1 is returned if the attribute is valid, 0 otherwise.  On
   4791  * failure, @link cupsLastErrorString@ is set to a human-readable message.
   4792  *
   4793  * @since CUPS 1.7/macOS 10.9@
   4794  */
   4795 
   4796 int					/* O - 1 if valid, 0 otherwise */
   4797 ippValidateAttribute(
   4798     ipp_attribute_t *attr)		/* I - Attribute */
   4799 {
   4800   int		i;			/* Looping var */
   4801   char		scheme[64],		/* Scheme from URI */
   4802 		userpass[256],		/* Username/password from URI */
   4803 		hostname[256],		/* Hostname from URI */
   4804 		resource[1024];		/* Resource from URI */
   4805   int		port,			/* Port number from URI */
   4806 		uri_status;		/* URI separation status */
   4807   const char	*ptr;			/* Pointer into string */
   4808   ipp_attribute_t *colattr;		/* Collection attribute */
   4809   regex_t	re;			/* Regular expression */
   4810   ipp_uchar_t	*date;			/* Current date value */
   4811   static const char * const uri_status_strings[] =
   4812   {					/* URI status strings */
   4813     "URI too large",
   4814     "Bad arguments to function",
   4815     "Bad resource in URI",
   4816     "Bad port number in URI",
   4817     "Bad hostname/address in URI",
   4818     "Bad username in URI",
   4819     "Bad scheme in URI",
   4820     "Bad/empty URI",
   4821     "OK",
   4822     "Missing scheme in URI",
   4823     "Unknown scheme in URI",
   4824     "Missing resource in URI"
   4825   };
   4826 
   4827 
   4828  /*
   4829   * Skip separators.
   4830   */
   4831 
   4832   if (!attr->name)
   4833     return (1);
   4834 
   4835  /*
   4836   * Validate the attribute name.
   4837   */
   4838 
   4839   for (ptr = attr->name; *ptr; ptr ++)
   4840     if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_')
   4841       break;
   4842 
   4843   if (*ptr || ptr == attr->name)
   4844   {
   4845     ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4846                   _("\"%s\": Bad attribute name - invalid character "
   4847 		    "(RFC 8011 section 5.1.4)."), attr->name);
   4848     return (0);
   4849   }
   4850 
   4851   if ((ptr - attr->name) > 255)
   4852   {
   4853     ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4854                   _("\"%s\": Bad attribute name - bad length %d "
   4855 		    "(RFC 8011 section 5.1.4)."), attr->name,
   4856 		  (int)(ptr - attr->name));
   4857     return (0);
   4858   }
   4859 
   4860   switch (attr->value_tag)
   4861   {
   4862     case IPP_TAG_INTEGER :
   4863         break;
   4864 
   4865     case IPP_TAG_BOOLEAN :
   4866         for (i = 0; i < attr->num_values; i ++)
   4867 	{
   4868 	  if (attr->values[i].boolean != 0 &&
   4869 	      attr->values[i].boolean != 1)
   4870 	  {
   4871 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4872                           _("\"%s\": Bad boolen value %d "
   4873 			    "(RFC 8011 section 5.1.21)."), attr->name,
   4874 			  attr->values[i].boolean);
   4875 	    return (0);
   4876 	  }
   4877 	}
   4878         break;
   4879 
   4880     case IPP_TAG_ENUM :
   4881         for (i = 0; i < attr->num_values; i ++)
   4882 	{
   4883 	  if (attr->values[i].integer < 1)
   4884 	  {
   4885 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4886 			  _("\"%s\": Bad enum value %d - out of range "
   4887 			    "(RFC 8011 section 5.1.5)."), attr->name,
   4888 			    attr->values[i].integer);
   4889             return (0);
   4890 	  }
   4891 	}
   4892         break;
   4893 
   4894     case IPP_TAG_STRING :
   4895         for (i = 0; i < attr->num_values; i ++)
   4896 	{
   4897 	  if (attr->values[i].unknown.length > IPP_MAX_OCTETSTRING)
   4898 	  {
   4899 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4900 			  _("\"%s\": Bad octetString value - bad length %d "
   4901 			    "(RFC 8011 section 5.1.20)."), attr->name,
   4902 			    attr->values[i].unknown.length);
   4903 	    return (0);
   4904 	  }
   4905 	}
   4906         break;
   4907 
   4908     case IPP_TAG_DATE :
   4909         for (i = 0; i < attr->num_values; i ++)
   4910 	{
   4911 	  date = attr->values[i].date;
   4912 
   4913           if (date[2] < 1 || date[2] > 12)
   4914 	  {
   4915 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4916 			  _("\"%s\": Bad dateTime month %u "
   4917 			    "(RFC 8011 section 5.1.15)."), attr->name, date[2]);
   4918 	    return (0);
   4919 	  }
   4920 
   4921           if (date[3] < 1 || date[3] > 31)
   4922 	  {
   4923 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4924 			  _("\"%s\": Bad dateTime day %u "
   4925 			    "(RFC 8011 section 5.1.15)."), attr->name, date[3]);
   4926 	    return (0);
   4927 	  }
   4928 
   4929           if (date[4] > 23)
   4930 	  {
   4931 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4932 			  _("\"%s\": Bad dateTime hours %u "
   4933 			    "(RFC 8011 section 5.1.15)."), attr->name, date[4]);
   4934 	    return (0);
   4935 	  }
   4936 
   4937           if (date[5] > 59)
   4938 	  {
   4939 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4940 			  _("\"%s\": Bad dateTime minutes %u "
   4941 			    "(RFC 8011 section 5.1.15)."), attr->name, date[5]);
   4942 	    return (0);
   4943 	  }
   4944 
   4945           if (date[6] > 60)
   4946 	  {
   4947 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4948 			  _("\"%s\": Bad dateTime seconds %u "
   4949 			    "(RFC 8011 section 5.1.15)."), attr->name, date[6]);
   4950 	    return (0);
   4951 	  }
   4952 
   4953           if (date[7] > 9)
   4954 	  {
   4955 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4956 			  _("\"%s\": Bad dateTime deciseconds %u "
   4957 			    "(RFC 8011 section 5.1.15)."), attr->name, date[7]);
   4958 	    return (0);
   4959 	  }
   4960 
   4961           if (date[8] != '-' && date[8] != '+')
   4962 	  {
   4963 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4964 			  _("\"%s\": Bad dateTime UTC sign '%c' "
   4965 			    "(RFC 8011 section 5.1.15)."), attr->name, date[8]);
   4966 	    return (0);
   4967 	  }
   4968 
   4969           if (date[9] > 11)
   4970 	  {
   4971 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4972 			  _("\"%s\": Bad dateTime UTC hours %u "
   4973 			    "(RFC 8011 section 5.1.15)."), attr->name, date[9]);
   4974 	    return (0);
   4975 	  }
   4976 
   4977           if (date[10] > 59)
   4978 	  {
   4979 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4980 			  _("\"%s\": Bad dateTime UTC minutes %u "
   4981 			    "(RFC 8011 section 5.1.15)."), attr->name, date[10]);
   4982 	    return (0);
   4983 	  }
   4984 	}
   4985         break;
   4986 
   4987     case IPP_TAG_RESOLUTION :
   4988         for (i = 0; i < attr->num_values; i ++)
   4989 	{
   4990 	  if (attr->values[i].resolution.xres <= 0)
   4991 	  {
   4992 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4993 			  _("\"%s\": Bad resolution value %dx%d%s - cross "
   4994 			    "feed resolution must be positive "
   4995 			    "(RFC 8011 section 5.1.16)."), attr->name,
   4996 			  attr->values[i].resolution.xres,
   4997 			  attr->values[i].resolution.yres,
   4998 			  attr->values[i].resolution.units ==
   4999 			      IPP_RES_PER_INCH ? "dpi" :
   5000 			      attr->values[i].resolution.units ==
   5001 				  IPP_RES_PER_CM ? "dpcm" : "unknown");
   5002 	    return (0);
   5003 	  }
   5004 
   5005 	  if (attr->values[i].resolution.yres <= 0)
   5006 	  {
   5007 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5008 			  _("\"%s\": Bad resolution value %dx%d%s - feed "
   5009 			    "resolution must be positive "
   5010 			    "(RFC 8011 section 5.1.16)."), attr->name,
   5011 			  attr->values[i].resolution.xres,
   5012 			  attr->values[i].resolution.yres,
   5013 			  attr->values[i].resolution.units ==
   5014 			      IPP_RES_PER_INCH ? "dpi" :
   5015 			      attr->values[i].resolution.units ==
   5016 				  IPP_RES_PER_CM ? "dpcm" : "unknown");
   5017             return (0);
   5018 	  }
   5019 
   5020 	  if (attr->values[i].resolution.units != IPP_RES_PER_INCH &&
   5021 	      attr->values[i].resolution.units != IPP_RES_PER_CM)
   5022 	  {
   5023 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5024 			  _("\"%s\": Bad resolution value %dx%d%s - bad "
   5025 			    "units value (RFC 8011 section 5.1.16)."),
   5026 			  attr->name, attr->values[i].resolution.xres,
   5027 			  attr->values[i].resolution.yres,
   5028 			  attr->values[i].resolution.units ==
   5029 			      IPP_RES_PER_INCH ? "dpi" :
   5030 			      attr->values[i].resolution.units ==
   5031 				  IPP_RES_PER_CM ? "dpcm" : "unknown");
   5032 	    return (0);
   5033 	  }
   5034 	}
   5035         break;
   5036 
   5037     case IPP_TAG_RANGE :
   5038         for (i = 0; i < attr->num_values; i ++)
   5039 	{
   5040 	  if (attr->values[i].range.lower > attr->values[i].range.upper)
   5041 	  {
   5042 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5043 			  _("\"%s\": Bad rangeOfInteger value %d-%d - lower "
   5044 			    "greater than upper (RFC 8011 section 5.1.14)."),
   5045 			  attr->name, attr->values[i].range.lower,
   5046 			  attr->values[i].range.upper);
   5047 	    return (0);
   5048 	  }
   5049 	}
   5050         break;
   5051 
   5052     case IPP_TAG_BEGIN_COLLECTION :
   5053         for (i = 0; i < attr->num_values; i ++)
   5054 	{
   5055 	  for (colattr = attr->values[i].collection->attrs;
   5056 	       colattr;
   5057 	       colattr = colattr->next)
   5058 	  {
   5059 	    if (!ippValidateAttribute(colattr))
   5060 	      return (0);
   5061 	  }
   5062 	}
   5063         break;
   5064 
   5065     case IPP_TAG_TEXT :
   5066     case IPP_TAG_TEXTLANG :
   5067         for (i = 0; i < attr->num_values; i ++)
   5068 	{
   5069 	  for (ptr = attr->values[i].string.text; *ptr; ptr ++)
   5070 	  {
   5071 	    if ((*ptr & 0xe0) == 0xc0)
   5072 	    {
   5073 	      ptr ++;
   5074 	      if ((*ptr & 0xc0) != 0x80)
   5075 	        break;
   5076 	    }
   5077 	    else if ((*ptr & 0xf0) == 0xe0)
   5078 	    {
   5079 	      ptr ++;
   5080 	      if ((*ptr & 0xc0) != 0x80)
   5081 	        break;
   5082 	      ptr ++;
   5083 	      if ((*ptr & 0xc0) != 0x80)
   5084 	        break;
   5085 	    }
   5086 	    else if ((*ptr & 0xf8) == 0xf0)
   5087 	    {
   5088 	      ptr ++;
   5089 	      if ((*ptr & 0xc0) != 0x80)
   5090 	        break;
   5091 	      ptr ++;
   5092 	      if ((*ptr & 0xc0) != 0x80)
   5093 	        break;
   5094 	      ptr ++;
   5095 	      if ((*ptr & 0xc0) != 0x80)
   5096 	        break;
   5097 	    }
   5098 	    else if (*ptr & 0x80)
   5099 	      break;
   5100 	  }
   5101 
   5102 	  if (*ptr)
   5103 	  {
   5104 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5105 			  _("\"%s\": Bad text value \"%s\" - bad UTF-8 "
   5106 			    "sequence (RFC 8011 section 5.1.2)."), attr->name,
   5107 			  attr->values[i].string.text);
   5108 	    return (0);
   5109 	  }
   5110 
   5111 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_TEXT - 1))
   5112 	  {
   5113 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5114 			  _("\"%s\": Bad text value \"%s\" - bad length %d "
   5115 			    "(RFC 8011 section 5.1.2)."), attr->name,
   5116 			  attr->values[i].string.text,
   5117 			  (int)(ptr - attr->values[i].string.text));
   5118 	    return (0);
   5119 	  }
   5120 	}
   5121         break;
   5122 
   5123     case IPP_TAG_NAME :
   5124     case IPP_TAG_NAMELANG :
   5125         for (i = 0; i < attr->num_values; i ++)
   5126 	{
   5127 	  for (ptr = attr->values[i].string.text; *ptr; ptr ++)
   5128 	  {
   5129 	    if ((*ptr & 0xe0) == 0xc0)
   5130 	    {
   5131 	      ptr ++;
   5132 	      if ((*ptr & 0xc0) != 0x80)
   5133 	        break;
   5134 	    }
   5135 	    else if ((*ptr & 0xf0) == 0xe0)
   5136 	    {
   5137 	      ptr ++;
   5138 	      if ((*ptr & 0xc0) != 0x80)
   5139 	        break;
   5140 	      ptr ++;
   5141 	      if ((*ptr & 0xc0) != 0x80)
   5142 	        break;
   5143 	    }
   5144 	    else if ((*ptr & 0xf8) == 0xf0)
   5145 	    {
   5146 	      ptr ++;
   5147 	      if ((*ptr & 0xc0) != 0x80)
   5148 	        break;
   5149 	      ptr ++;
   5150 	      if ((*ptr & 0xc0) != 0x80)
   5151 	        break;
   5152 	      ptr ++;
   5153 	      if ((*ptr & 0xc0) != 0x80)
   5154 	        break;
   5155 	    }
   5156 	    else if (*ptr & 0x80)
   5157 	      break;
   5158 	  }
   5159 
   5160 	  if (*ptr)
   5161 	  {
   5162 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5163 			  _("\"%s\": Bad name value \"%s\" - bad UTF-8 "
   5164 			    "sequence (RFC 8011 section 5.1.3)."), attr->name,
   5165 			  attr->values[i].string.text);
   5166 	    return (0);
   5167 	  }
   5168 
   5169 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_NAME - 1))
   5170 	  {
   5171 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5172 			  _("\"%s\": Bad name value \"%s\" - bad length %d "
   5173 			    "(RFC 8011 section 5.1.3)."), attr->name,
   5174 			  attr->values[i].string.text,
   5175 			  (int)(ptr - attr->values[i].string.text));
   5176 	    return (0);
   5177 	  }
   5178 	}
   5179         break;
   5180 
   5181     case IPP_TAG_KEYWORD :
   5182         for (i = 0; i < attr->num_values; i ++)
   5183 	{
   5184 	  for (ptr = attr->values[i].string.text; *ptr; ptr ++)
   5185 	    if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' &&
   5186 	        *ptr != '_')
   5187 	      break;
   5188 
   5189 	  if (*ptr || ptr == attr->values[i].string.text)
   5190 	  {
   5191 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5192 			  _("\"%s\": Bad keyword value \"%s\" - invalid "
   5193 			    "character (RFC 8011 section 5.1.4)."),
   5194 			  attr->name, attr->values[i].string.text);
   5195 	    return (0);
   5196 	  }
   5197 
   5198 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_KEYWORD - 1))
   5199 	  {
   5200 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5201 			  _("\"%s\": Bad keyword value \"%s\" - bad "
   5202 			    "length %d (RFC 8011 section 5.1.4)."),
   5203 			  attr->name, attr->values[i].string.text,
   5204 			  (int)(ptr - attr->values[i].string.text));
   5205 	    return (0);
   5206 	  }
   5207 	}
   5208         break;
   5209 
   5210     case IPP_TAG_URI :
   5211         for (i = 0; i < attr->num_values; i ++)
   5212 	{
   5213 	  uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
   5214 	                               attr->values[i].string.text,
   5215 				       scheme, sizeof(scheme),
   5216 				       userpass, sizeof(userpass),
   5217 				       hostname, sizeof(hostname),
   5218 				       &port, resource, sizeof(resource));
   5219 
   5220 	  if (uri_status < HTTP_URI_STATUS_OK)
   5221 	  {
   5222 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5223 			  _("\"%s\": Bad URI value \"%s\" - %s "
   5224 			    "(RFC 8011 section 5.1.6)."), attr->name,
   5225 			  attr->values[i].string.text,
   5226 			  uri_status_strings[uri_status -
   5227 					     HTTP_URI_STATUS_OVERFLOW]);
   5228 	    return (0);
   5229 	  }
   5230 
   5231 	  if (strlen(attr->values[i].string.text) > (IPP_MAX_URI - 1))
   5232 	  {
   5233 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5234 			  _("\"%s\": Bad URI value \"%s\" - bad length %d "
   5235 			    "(RFC 8011 section 5.1.6)."), attr->name,
   5236 			  attr->values[i].string.text,
   5237 			  (int)strlen(attr->values[i].string.text));
   5238 	  }
   5239 	}
   5240         break;
   5241 
   5242     case IPP_TAG_URISCHEME :
   5243         for (i = 0; i < attr->num_values; i ++)
   5244 	{
   5245 	  ptr = attr->values[i].string.text;
   5246 	  if (islower(*ptr & 255))
   5247 	  {
   5248 	    for (ptr ++; *ptr; ptr ++)
   5249 	      if (!islower(*ptr & 255) && !isdigit(*ptr & 255) &&
   5250 	          *ptr != '+' && *ptr != '-' && *ptr != '.')
   5251                 break;
   5252 	  }
   5253 
   5254 	  if (*ptr || ptr == attr->values[i].string.text)
   5255 	  {
   5256 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5257 			  _("\"%s\": Bad uriScheme value \"%s\" - bad "
   5258 			    "characters (RFC 8011 section 5.1.7)."),
   5259 			  attr->name, attr->values[i].string.text);
   5260 	    return (0);
   5261 	  }
   5262 
   5263 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_URISCHEME - 1))
   5264 	  {
   5265 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5266 			  _("\"%s\": Bad uriScheme value \"%s\" - bad "
   5267 			    "length %d (RFC 8011 section 5.1.7)."),
   5268 			  attr->name, attr->values[i].string.text,
   5269 			  (int)(ptr - attr->values[i].string.text));
   5270 	    return (0);
   5271 	  }
   5272 	}
   5273         break;
   5274 
   5275     case IPP_TAG_CHARSET :
   5276         for (i = 0; i < attr->num_values; i ++)
   5277 	{
   5278 	  for (ptr = attr->values[i].string.text; *ptr; ptr ++)
   5279 	    if (!isprint(*ptr & 255) || isupper(*ptr & 255) ||
   5280 	        isspace(*ptr & 255))
   5281 	      break;
   5282 
   5283 	  if (*ptr || ptr == attr->values[i].string.text)
   5284 	  {
   5285 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5286 			  _("\"%s\": Bad charset value \"%s\" - bad "
   5287 			    "characters (RFC 8011 section 5.1.8)."),
   5288 			  attr->name, attr->values[i].string.text);
   5289 	    return (0);
   5290 	  }
   5291 
   5292 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_CHARSET - 1))
   5293 	  {
   5294 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5295 			  _("\"%s\": Bad charset value \"%s\" - bad "
   5296 			    "length %d (RFC 8011 section 5.1.8)."),
   5297 			  attr->name, attr->values[i].string.text,
   5298 			  (int)(ptr - attr->values[i].string.text));
   5299 	    return (0);
   5300 	  }
   5301 	}
   5302         break;
   5303 
   5304     case IPP_TAG_LANGUAGE :
   5305        /*
   5306         * The following regular expression is derived from the ABNF for
   5307 	* language tags in RFC 4646.  All I can say is that this is the
   5308 	* easiest way to check the values...
   5309 	*/
   5310 
   5311         if ((i = regcomp(&re,
   5312 			 "^("
   5313 			 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
   5314 								/* language */
   5315 			 "(-[a-z][a-z][a-z][a-z]){0,1}"		/* script */
   5316 			 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}"	/* region */
   5317 			 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*"	/* variant */
   5318 			 "(-[a-wy-z](-[a-z0-9]{2,8})+)*"	/* extension */
   5319 			 "(-x(-[a-z0-9]{1,8})+)*"		/* privateuse */
   5320 			 "|"
   5321 			 "x(-[a-z0-9]{1,8})+"			/* privateuse */
   5322 			 "|"
   5323 			 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}"	/* grandfathered */
   5324 			 ")$",
   5325 			 REG_NOSUB | REG_EXTENDED)) != 0)
   5326         {
   5327           char	temp[256];		/* Temporary error string */
   5328 
   5329           regerror(i, &re, temp, sizeof(temp));
   5330 	  ipp_set_error(IPP_STATUS_ERROR_INTERNAL,
   5331 			_("Unable to compile naturalLanguage regular "
   5332 			  "expression: %s."), temp);
   5333 	  return (0);
   5334         }
   5335 
   5336         for (i = 0; i < attr->num_values; i ++)
   5337 	{
   5338 	  if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
   5339 	  {
   5340 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5341 			  _("\"%s\": Bad naturalLanguage value \"%s\" - bad "
   5342 			    "characters (RFC 8011 section 5.1.9)."),
   5343 			  attr->name, attr->values[i].string.text);
   5344 	    regfree(&re);
   5345 	    return (0);
   5346 	  }
   5347 
   5348 	  if (strlen(attr->values[i].string.text) > (IPP_MAX_LANGUAGE - 1))
   5349 	  {
   5350 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5351 			  _("\"%s\": Bad naturalLanguage value \"%s\" - bad "
   5352 			    "length %d (RFC 8011 section 5.1.9)."),
   5353 			  attr->name, attr->values[i].string.text,
   5354 			  (int)strlen(attr->values[i].string.text));
   5355 	    regfree(&re);
   5356 	    return (0);
   5357 	  }
   5358 	}
   5359 
   5360 	regfree(&re);
   5361         break;
   5362 
   5363     case IPP_TAG_MIMETYPE :
   5364        /*
   5365         * The following regular expression is derived from the ABNF for
   5366 	* MIME media types in RFC 2045 and 4288.  All I can say is that this is
   5367 	* the easiest way to check the values...
   5368 	*/
   5369 
   5370         if ((i = regcomp(&re,
   5371 			 "^"
   5372 			 "[-a-zA-Z0-9!#$&.+^_]{1,127}"		/* type-name */
   5373 			 "/"
   5374 			 "[-a-zA-Z0-9!#$&.+^_]{1,127}"		/* subtype-name */
   5375 			 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}="	/* parameter= */
   5376 			 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
   5377 			 					/* value */
   5378 			 "$",
   5379 			 REG_NOSUB | REG_EXTENDED)) != 0)
   5380         {
   5381           char	temp[256];		/* Temporary error string */
   5382 
   5383           regerror(i, &re, temp, sizeof(temp));
   5384 	  ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5385 			_("Unable to compile mimeMediaType regular "
   5386 			  "expression: %s."), temp);
   5387 	  return (0);
   5388         }
   5389 
   5390         for (i = 0; i < attr->num_values; i ++)
   5391 	{
   5392 	  if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
   5393 	  {
   5394 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5395 			  _("\"%s\": Bad mimeMediaType value \"%s\" - bad "
   5396 			    "characters (RFC 8011 section 5.1.10)."),
   5397 			  attr->name, attr->values[i].string.text);
   5398 	    regfree(&re);
   5399 	    return (0);
   5400 	  }
   5401 
   5402 	  if (strlen(attr->values[i].string.text) > (IPP_MAX_MIMETYPE - 1))
   5403 	  {
   5404 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5405 			  _("\"%s\": Bad mimeMediaType value \"%s\" - bad "
   5406 			    "length %d (RFC 8011 section 5.1.10)."),
   5407 			  attr->name, attr->values[i].string.text,
   5408 			  (int)strlen(attr->values[i].string.text));
   5409 	    regfree(&re);
   5410 	    return (0);
   5411 	  }
   5412 	}
   5413 
   5414 	regfree(&re);
   5415         break;
   5416 
   5417     default :
   5418         break;
   5419   }
   5420 
   5421   return (1);
   5422 }
   5423 
   5424 
   5425 /*
   5426  * 'ippValidateAttributes()' - Validate all attributes in an IPP message.
   5427  *
   5428  * This function validates the contents of the IPP message, including each
   5429  * attribute.  Like @link ippValidateAttribute@, @link cupsLastErrorString@ is
   5430  * set to a human-readable message on failure.
   5431  *
   5432  * @since CUPS 1.7/macOS 10.9@
   5433  */
   5434 
   5435 int					/* O - 1 if valid, 0 otherwise */
   5436 ippValidateAttributes(ipp_t *ipp)	/* I - IPP message */
   5437 {
   5438   ipp_attribute_t	*attr;		/* Current attribute */
   5439 
   5440 
   5441   if (!ipp)
   5442     return (1);
   5443 
   5444   for (attr = ipp->attrs; attr; attr = attr->next)
   5445     if (!ippValidateAttribute(attr))
   5446       return (0);
   5447 
   5448   return (1);
   5449 }
   5450 
   5451 
   5452 /*
   5453  * 'ippWrite()' - Write data for an IPP message to a HTTP connection.
   5454  */
   5455 
   5456 ipp_state_t				/* O - Current state */
   5457 ippWrite(http_t *http,			/* I - HTTP connection */
   5458          ipp_t  *ipp)			/* I - IPP data */
   5459 {
   5460   DEBUG_printf(("ippWrite(http=%p, ipp=%p)", (void *)http, (void *)ipp));
   5461 
   5462   if (!http)
   5463     return (IPP_STATE_ERROR);
   5464 
   5465   return (ippWriteIO(http, (ipp_iocb_t)httpWrite2, http->blocking, NULL, ipp));
   5466 }
   5467 
   5468 
   5469 /*
   5470  * 'ippWriteFile()' - Write data for an IPP message to a file.
   5471  *
   5472  * @since CUPS 1.1.19/macOS 10.3@
   5473  */
   5474 
   5475 ipp_state_t				/* O - Current state */
   5476 ippWriteFile(int   fd,			/* I - HTTP data */
   5477              ipp_t *ipp)		/* I - IPP data */
   5478 {
   5479   DEBUG_printf(("ippWriteFile(fd=%d, ipp=%p)", fd, (void *)ipp));
   5480 
   5481   ipp->state = IPP_STATE_IDLE;
   5482 
   5483   return (ippWriteIO(&fd, (ipp_iocb_t)ipp_write_file, 1, NULL, ipp));
   5484 }
   5485 
   5486 
   5487 /*
   5488  * 'ippWriteIO()' - Write data for an IPP message.
   5489  *
   5490  * @since CUPS 1.2/macOS 10.5@
   5491  */
   5492 
   5493 ipp_state_t				/* O - Current state */
   5494 ippWriteIO(void       *dst,		/* I - Destination */
   5495            ipp_iocb_t cb,		/* I - Write callback function */
   5496 	   int        blocking,		/* I - Use blocking IO? */
   5497 	   ipp_t      *parent,		/* I - Parent IPP message */
   5498            ipp_t      *ipp)		/* I - IPP data */
   5499 {
   5500   int			i;		/* Looping var */
   5501   int			n;		/* Length of data */
   5502   unsigned char		*buffer,	/* Data buffer */
   5503 			*bufptr;	/* Pointer into buffer */
   5504   ipp_attribute_t	*attr;		/* Current attribute */
   5505   _ipp_value_t		*value;		/* Current value */
   5506 
   5507 
   5508   DEBUG_printf(("ippWriteIO(dst=%p, cb=%p, blocking=%d, parent=%p, ipp=%p)", (void *)dst, (void *)cb, blocking, (void *)parent, (void *)ipp));
   5509 
   5510   if (!dst || !ipp)
   5511     return (IPP_STATE_ERROR);
   5512 
   5513   if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL)
   5514   {
   5515     DEBUG_puts("1ippWriteIO: Unable to get write buffer");
   5516     return (IPP_STATE_ERROR);
   5517   }
   5518 
   5519   switch (ipp->state)
   5520   {
   5521     case IPP_STATE_IDLE :
   5522         ipp->state ++; /* Avoid common problem... */
   5523 
   5524     case IPP_STATE_HEADER :
   5525         if (parent == NULL)
   5526 	{
   5527 	 /*
   5528 	  * Send the request header:
   5529 	  *
   5530 	  *                 Version = 2 bytes
   5531 	  *   Operation/Status Code = 2 bytes
   5532 	  *              Request ID = 4 bytes
   5533 	  *                   Total = 8 bytes
   5534 	  */
   5535 
   5536           bufptr = buffer;
   5537 
   5538 	  *bufptr++ = ipp->request.any.version[0];
   5539 	  *bufptr++ = ipp->request.any.version[1];
   5540 	  *bufptr++ = (ipp_uchar_t)(ipp->request.any.op_status >> 8);
   5541 	  *bufptr++ = (ipp_uchar_t)ipp->request.any.op_status;
   5542 	  *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 24);
   5543 	  *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 16);
   5544 	  *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 8);
   5545 	  *bufptr++ = (ipp_uchar_t)ipp->request.any.request_id;
   5546 
   5547 	  DEBUG_printf(("2ippWriteIO: version=%d.%d", buffer[0], buffer[1]));
   5548 	  DEBUG_printf(("2ippWriteIO: op_status=%04x",
   5549 			ipp->request.any.op_status));
   5550 	  DEBUG_printf(("2ippWriteIO: request_id=%d",
   5551 			ipp->request.any.request_id));
   5552 
   5553           if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5554 	  {
   5555 	    DEBUG_puts("1ippWriteIO: Could not write IPP header...");
   5556 	    _cupsBufferRelease((char *)buffer);
   5557 	    return (IPP_STATE_ERROR);
   5558 	  }
   5559 	}
   5560 
   5561        /*
   5562 	* Reset the state engine to point to the first attribute
   5563 	* in the request/response, with no current group.
   5564 	*/
   5565 
   5566         ipp->state   = IPP_STATE_ATTRIBUTE;
   5567 	ipp->current = ipp->attrs;
   5568 	ipp->curtag  = IPP_TAG_ZERO;
   5569 
   5570 	DEBUG_printf(("1ippWriteIO: ipp->current=%p", (void *)ipp->current));
   5571 
   5572        /*
   5573         * If blocking is disabled, stop here...
   5574 	*/
   5575 
   5576         if (!blocking)
   5577 	  break;
   5578 
   5579     case IPP_STATE_ATTRIBUTE :
   5580         while (ipp->current != NULL)
   5581 	{
   5582 	 /*
   5583 	  * Write this attribute...
   5584 	  */
   5585 
   5586 	  bufptr = buffer;
   5587 	  attr   = ipp->current;
   5588 
   5589 	  ipp->current = ipp->current->next;
   5590 
   5591           if (!parent)
   5592 	  {
   5593 	    if (ipp->curtag != attr->group_tag)
   5594 	    {
   5595 	     /*
   5596 	      * Send a group tag byte...
   5597 	      */
   5598 
   5599 	      ipp->curtag = attr->group_tag;
   5600 
   5601 	      if (attr->group_tag == IPP_TAG_ZERO)
   5602 		continue;
   5603 
   5604 	      DEBUG_printf(("2ippWriteIO: wrote group tag=%x(%s)",
   5605 			    attr->group_tag, ippTagString(attr->group_tag)));
   5606 	      *bufptr++ = (ipp_uchar_t)attr->group_tag;
   5607 	    }
   5608 	    else if (attr->group_tag == IPP_TAG_ZERO)
   5609 	      continue;
   5610 	  }
   5611 
   5612 	  DEBUG_printf(("1ippWriteIO: %s (%s%s)", attr->name,
   5613 	                attr->num_values > 1 ? "1setOf " : "",
   5614 			ippTagString(attr->value_tag)));
   5615 
   5616          /*
   5617 	  * Write the attribute tag and name.
   5618 	  *
   5619 	  * The attribute name length does not include the trailing nul
   5620 	  * character in the source string.
   5621 	  *
   5622 	  * Collection values (parent != NULL) are written differently...
   5623 	  */
   5624 
   5625           if (parent == NULL)
   5626 	  {
   5627            /*
   5628 	    * Get the length of the attribute name, and make sure it won't
   5629 	    * overflow the buffer...
   5630 	    */
   5631 
   5632             if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 8))
   5633 	    {
   5634 	      DEBUG_printf(("1ippWriteIO: Attribute name too long (%d)", n));
   5635 	      _cupsBufferRelease((char *)buffer);
   5636 	      return (IPP_STATE_ERROR);
   5637 	    }
   5638 
   5639            /*
   5640 	    * Write the value tag, name length, and name string...
   5641 	    */
   5642 
   5643             DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
   5644 	                  attr->value_tag, ippTagString(attr->value_tag)));
   5645             DEBUG_printf(("2ippWriteIO: writing name=%d,\"%s\"", n,
   5646 	                  attr->name));
   5647 
   5648             if (attr->value_tag > 0xff)
   5649             {
   5650               *bufptr++ = IPP_TAG_EXTENSION;
   5651 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 24);
   5652 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 16);
   5653 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 8);
   5654 	      *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5655             }
   5656             else
   5657 	      *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5658 
   5659 	    *bufptr++ = (ipp_uchar_t)(n >> 8);
   5660 	    *bufptr++ = (ipp_uchar_t)n;
   5661 	    memcpy(bufptr, attr->name, (size_t)n);
   5662 	    bufptr += n;
   5663           }
   5664 	  else
   5665 	  {
   5666            /*
   5667 	    * Get the length of the attribute name, and make sure it won't
   5668 	    * overflow the buffer...
   5669 	    */
   5670 
   5671             if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 12))
   5672 	    {
   5673 	      DEBUG_printf(("1ippWriteIO: Attribute name too long (%d)", n));
   5674 	      _cupsBufferRelease((char *)buffer);
   5675 	      return (IPP_STATE_ERROR);
   5676 	    }
   5677 
   5678            /*
   5679 	    * Write the member name tag, name length, name string, value tag,
   5680 	    * and empty name for the collection member attribute...
   5681 	    */
   5682 
   5683             DEBUG_printf(("2ippWriteIO: writing value tag=%x(memberName)",
   5684 	                  IPP_TAG_MEMBERNAME));
   5685             DEBUG_printf(("2ippWriteIO: writing name=%d,\"%s\"", n,
   5686 	                  attr->name));
   5687             DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
   5688 	                  attr->value_tag, ippTagString(attr->value_tag)));
   5689             DEBUG_puts("2ippWriteIO: writing name=0,\"\"");
   5690 
   5691             *bufptr++ = IPP_TAG_MEMBERNAME;
   5692 	    *bufptr++ = 0;
   5693 	    *bufptr++ = 0;
   5694 	    *bufptr++ = (ipp_uchar_t)(n >> 8);
   5695 	    *bufptr++ = (ipp_uchar_t)n;
   5696 	    memcpy(bufptr, attr->name, (size_t)n);
   5697 	    bufptr += n;
   5698 
   5699             if (attr->value_tag > 0xff)
   5700             {
   5701               *bufptr++ = IPP_TAG_EXTENSION;
   5702 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 24);
   5703 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 16);
   5704 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 8);
   5705 	      *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5706             }
   5707             else
   5708 	      *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5709 
   5710             *bufptr++ = 0;
   5711             *bufptr++ = 0;
   5712 	  }
   5713 
   5714          /*
   5715 	  * Now write the attribute value(s)...
   5716 	  */
   5717 
   5718 	  switch (attr->value_tag & ~IPP_TAG_CUPS_CONST)
   5719 	  {
   5720 	    case IPP_TAG_UNSUPPORTED_VALUE :
   5721 	    case IPP_TAG_DEFAULT :
   5722 	    case IPP_TAG_UNKNOWN :
   5723 	    case IPP_TAG_NOVALUE :
   5724 	    case IPP_TAG_NOTSETTABLE :
   5725 	    case IPP_TAG_DELETEATTR :
   5726 	    case IPP_TAG_ADMINDEFINE :
   5727 		*bufptr++ = 0;
   5728 		*bufptr++ = 0;
   5729 	        break;
   5730 
   5731 	    case IPP_TAG_INTEGER :
   5732 	    case IPP_TAG_ENUM :
   5733 	        for (i = 0, value = attr->values;
   5734 		     i < attr->num_values;
   5735 		     i ++, value ++)
   5736 		{
   5737                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 9)
   5738 		  {
   5739                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5740 	            {
   5741 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   5742 		                 "attribute...");
   5743 		      _cupsBufferRelease((char *)buffer);
   5744 	              return (IPP_STATE_ERROR);
   5745 	            }
   5746 
   5747 		    bufptr = buffer;
   5748 		  }
   5749 
   5750 		  if (i)
   5751 		  {
   5752 		   /*
   5753 		    * Arrays and sets are done by sending additional
   5754 		    * values with a zero-length name...
   5755 		    */
   5756 
   5757                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5758 		    *bufptr++ = 0;
   5759 		    *bufptr++ = 0;
   5760 		  }
   5761 
   5762 		 /*
   5763 	          * Integers and enumerations are both 4-byte signed
   5764 		  * (twos-complement) values.
   5765 		  *
   5766 		  * Put the 2-byte length and 4-byte value into the buffer...
   5767 		  */
   5768 
   5769 	          *bufptr++ = 0;
   5770 		  *bufptr++ = 4;
   5771 		  *bufptr++ = (ipp_uchar_t)(value->integer >> 24);
   5772 		  *bufptr++ = (ipp_uchar_t)(value->integer >> 16);
   5773 		  *bufptr++ = (ipp_uchar_t)(value->integer >> 8);
   5774 		  *bufptr++ = (ipp_uchar_t)value->integer;
   5775 		}
   5776 		break;
   5777 
   5778 	    case IPP_TAG_BOOLEAN :
   5779 	        for (i = 0, value = attr->values;
   5780 		     i < attr->num_values;
   5781 		     i ++, value ++)
   5782 		{
   5783                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 6)
   5784 		  {
   5785                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5786 	            {
   5787 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   5788 		                 "attribute...");
   5789 		      _cupsBufferRelease((char *)buffer);
   5790 	              return (IPP_STATE_ERROR);
   5791 	            }
   5792 
   5793 		    bufptr = buffer;
   5794 		  }
   5795 
   5796 		  if (i)
   5797 		  {
   5798 		   /*
   5799 		    * Arrays and sets are done by sending additional
   5800 		    * values with a zero-length name...
   5801 		    */
   5802 
   5803                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5804 		    *bufptr++ = 0;
   5805 		    *bufptr++ = 0;
   5806 		  }
   5807 
   5808                  /*
   5809 		  * Boolean values are 1-byte; 0 = false, 1 = true.
   5810 		  *
   5811 		  * Put the 2-byte length and 1-byte value into the buffer...
   5812 		  */
   5813 
   5814 	          *bufptr++ = 0;
   5815 		  *bufptr++ = 1;
   5816 		  *bufptr++ = (ipp_uchar_t)value->boolean;
   5817 		}
   5818 		break;
   5819 
   5820 	    case IPP_TAG_TEXT :
   5821 	    case IPP_TAG_NAME :
   5822 	    case IPP_TAG_KEYWORD :
   5823 	    case IPP_TAG_URI :
   5824 	    case IPP_TAG_URISCHEME :
   5825 	    case IPP_TAG_CHARSET :
   5826 	    case IPP_TAG_LANGUAGE :
   5827 	    case IPP_TAG_MIMETYPE :
   5828 	        for (i = 0, value = attr->values;
   5829 		     i < attr->num_values;
   5830 		     i ++, value ++)
   5831 		{
   5832 		  if (i)
   5833 		  {
   5834 		   /*
   5835 		    * Arrays and sets are done by sending additional
   5836 		    * values with a zero-length name...
   5837 		    */
   5838 
   5839         	    DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
   5840 		                  attr->value_tag,
   5841 				  ippTagString(attr->value_tag)));
   5842         	    DEBUG_printf(("2ippWriteIO: writing name=0,\"\""));
   5843 
   5844                     if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
   5845 		    {
   5846                       if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5847 	              {
   5848 	        	DEBUG_puts("1ippWriteIO: Could not write IPP "
   5849 			           "attribute...");
   5850 			_cupsBufferRelease((char *)buffer);
   5851 	        	return (IPP_STATE_ERROR);
   5852 	              }
   5853 
   5854 		      bufptr = buffer;
   5855 		    }
   5856 
   5857                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5858 		    *bufptr++ = 0;
   5859 		    *bufptr++ = 0;
   5860 		  }
   5861 
   5862                   if (value->string.text != NULL)
   5863                     n = (int)strlen(value->string.text);
   5864 		  else
   5865 		    n = 0;
   5866 
   5867                   if (n > (IPP_BUF_SIZE - 2))
   5868 		  {
   5869 		    DEBUG_printf(("1ippWriteIO: String too long (%d)", n));
   5870 		    _cupsBufferRelease((char *)buffer);
   5871 		    return (IPP_STATE_ERROR);
   5872 		  }
   5873 
   5874                   DEBUG_printf(("2ippWriteIO: writing string=%d,\"%s\"", n,
   5875 		                value->string.text));
   5876 
   5877                   if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
   5878 		  {
   5879                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5880 	            {
   5881 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   5882 		                 "attribute...");
   5883 		      _cupsBufferRelease((char *)buffer);
   5884 	              return (IPP_STATE_ERROR);
   5885 	            }
   5886 
   5887 		    bufptr = buffer;
   5888 		  }
   5889 
   5890 		 /*
   5891 		  * All simple strings consist of the 2-byte length and
   5892 		  * character data without the trailing nul normally found
   5893 		  * in C strings.  Also, strings cannot be longer than IPP_MAX_LENGTH
   5894 		  * bytes since the 2-byte length is a signed (twos-complement)
   5895 		  * value.
   5896 		  *
   5897 		  * Put the 2-byte length and string characters in the buffer.
   5898 		  */
   5899 
   5900 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
   5901 		  *bufptr++ = (ipp_uchar_t)n;
   5902 
   5903 		  if (n > 0)
   5904 		  {
   5905 		    memcpy(bufptr, value->string.text, (size_t)n);
   5906 		    bufptr += n;
   5907 		  }
   5908 		}
   5909 		break;
   5910 
   5911 	    case IPP_TAG_DATE :
   5912 	        for (i = 0, value = attr->values;
   5913 		     i < attr->num_values;
   5914 		     i ++, value ++)
   5915 		{
   5916                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 16)
   5917 		  {
   5918                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5919 	            {
   5920 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   5921 		                 "attribute...");
   5922 		      _cupsBufferRelease((char *)buffer);
   5923 	              return (IPP_STATE_ERROR);
   5924 	            }
   5925 
   5926 		    bufptr = buffer;
   5927 		  }
   5928 
   5929 		  if (i)
   5930 		  {
   5931 		   /*
   5932 		    * Arrays and sets are done by sending additional
   5933 		    * values with a zero-length name...
   5934 		    */
   5935 
   5936                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5937 		    *bufptr++ = 0;
   5938 		    *bufptr++ = 0;
   5939 		  }
   5940 
   5941                  /*
   5942 		  * Date values consist of a 2-byte length and an
   5943 		  * 11-byte date/time structure defined by RFC 1903.
   5944 		  *
   5945 		  * Put the 2-byte length and 11-byte date/time
   5946 		  * structure in the buffer.
   5947 		  */
   5948 
   5949 	          *bufptr++ = 0;
   5950 		  *bufptr++ = 11;
   5951 		  memcpy(bufptr, value->date, 11);
   5952 		  bufptr += 11;
   5953 		}
   5954 		break;
   5955 
   5956 	    case IPP_TAG_RESOLUTION :
   5957 	        for (i = 0, value = attr->values;
   5958 		     i < attr->num_values;
   5959 		     i ++, value ++)
   5960 		{
   5961                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 14)
   5962 		  {
   5963                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5964 	            {
   5965 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   5966 		                 "attribute...");
   5967 		      _cupsBufferRelease((char *)buffer);
   5968 		      return (IPP_STATE_ERROR);
   5969 	            }
   5970 
   5971 		    bufptr = buffer;
   5972 		  }
   5973 
   5974 		  if (i)
   5975 		  {
   5976 		   /*
   5977 		    * Arrays and sets are done by sending additional
   5978 		    * values with a zero-length name...
   5979 		    */
   5980 
   5981                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5982 		    *bufptr++ = 0;
   5983 		    *bufptr++ = 0;
   5984 		  }
   5985 
   5986                  /*
   5987 		  * Resolution values consist of a 2-byte length,
   5988 		  * 4-byte horizontal resolution value, 4-byte vertical
   5989 		  * resolution value, and a 1-byte units value.
   5990 		  *
   5991 		  * Put the 2-byte length and resolution value data
   5992 		  * into the buffer.
   5993 		  */
   5994 
   5995 	          *bufptr++ = 0;
   5996 		  *bufptr++ = 9;
   5997 		  *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 24);
   5998 		  *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 16);
   5999 		  *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 8);
   6000 		  *bufptr++ = (ipp_uchar_t)value->resolution.xres;
   6001 		  *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 24);
   6002 		  *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 16);
   6003 		  *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 8);
   6004 		  *bufptr++ = (ipp_uchar_t)value->resolution.yres;
   6005 		  *bufptr++ = (ipp_uchar_t)value->resolution.units;
   6006 		}
   6007 		break;
   6008 
   6009 	    case IPP_TAG_RANGE :
   6010 	        for (i = 0, value = attr->values;
   6011 		     i < attr->num_values;
   6012 		     i ++, value ++)
   6013 		{
   6014                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 13)
   6015 		  {
   6016                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6017 	            {
   6018 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   6019 		                 "attribute...");
   6020 		      _cupsBufferRelease((char *)buffer);
   6021 	              return (IPP_STATE_ERROR);
   6022 	            }
   6023 
   6024 		    bufptr = buffer;
   6025 		  }
   6026 
   6027 		  if (i)
   6028 		  {
   6029 		   /*
   6030 		    * Arrays and sets are done by sending additional
   6031 		    * values with a zero-length name...
   6032 		    */
   6033 
   6034                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   6035 		    *bufptr++ = 0;
   6036 		    *bufptr++ = 0;
   6037 		  }
   6038 
   6039                  /*
   6040 		  * Range values consist of a 2-byte length,
   6041 		  * 4-byte lower value, and 4-byte upper value.
   6042 		  *
   6043 		  * Put the 2-byte length and range value data
   6044 		  * into the buffer.
   6045 		  */
   6046 
   6047 	          *bufptr++ = 0;
   6048 		  *bufptr++ = 8;
   6049 		  *bufptr++ = (ipp_uchar_t)(value->range.lower >> 24);
   6050 		  *bufptr++ = (ipp_uchar_t)(value->range.lower >> 16);
   6051 		  *bufptr++ = (ipp_uchar_t)(value->range.lower >> 8);
   6052 		  *bufptr++ = (ipp_uchar_t)value->range.lower;
   6053 		  *bufptr++ = (ipp_uchar_t)(value->range.upper >> 24);
   6054 		  *bufptr++ = (ipp_uchar_t)(value->range.upper >> 16);
   6055 		  *bufptr++ = (ipp_uchar_t)(value->range.upper >> 8);
   6056 		  *bufptr++ = (ipp_uchar_t)value->range.upper;
   6057 		}
   6058 		break;
   6059 
   6060 	    case IPP_TAG_TEXTLANG :
   6061 	    case IPP_TAG_NAMELANG :
   6062 	        for (i = 0, value = attr->values;
   6063 		     i < attr->num_values;
   6064 		     i ++, value ++)
   6065 		{
   6066 		  if (i)
   6067 		  {
   6068 		   /*
   6069 		    * Arrays and sets are done by sending additional
   6070 		    * values with a zero-length name...
   6071 		    */
   6072 
   6073                     if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
   6074 		    {
   6075                       if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6076 	              {
   6077 	        	DEBUG_puts("1ippWriteIO: Could not write IPP "
   6078 		                   "attribute...");
   6079 			_cupsBufferRelease((char *)buffer);
   6080 	        	return (IPP_STATE_ERROR);
   6081 	              }
   6082 
   6083 		      bufptr = buffer;
   6084 		    }
   6085 
   6086                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   6087 		    *bufptr++ = 0;
   6088 		    *bufptr++ = 0;
   6089 		  }
   6090 
   6091                  /*
   6092 		  * textWithLanguage and nameWithLanguage values consist
   6093 		  * of a 2-byte length for both strings and their
   6094 		  * individual lengths, a 2-byte length for the
   6095 		  * character string, the character string without the
   6096 		  * trailing nul, a 2-byte length for the character
   6097 		  * set string, and the character set string without
   6098 		  * the trailing nul.
   6099 		  */
   6100 
   6101                   n = 4;
   6102 
   6103 		  if (value->string.language != NULL)
   6104                     n += (int)strlen(value->string.language);
   6105 
   6106 		  if (value->string.text != NULL)
   6107                     n += (int)strlen(value->string.text);
   6108 
   6109                   if (n > (IPP_BUF_SIZE - 2))
   6110 		  {
   6111 		    DEBUG_printf(("1ippWriteIO: text/nameWithLanguage value "
   6112 		                  "too long (%d)", n));
   6113 		    _cupsBufferRelease((char *)buffer);
   6114 		    return (IPP_STATE_ERROR);
   6115                   }
   6116 
   6117                   if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
   6118 		  {
   6119                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6120 	            {
   6121 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   6122 		                 "attribute...");
   6123 		      _cupsBufferRelease((char *)buffer);
   6124 	              return (IPP_STATE_ERROR);
   6125 	            }
   6126 
   6127 		    bufptr = buffer;
   6128 		  }
   6129 
   6130                  /* Length of entire value */
   6131 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
   6132 		  *bufptr++ = (ipp_uchar_t)n;
   6133 
   6134                  /* Length of language */
   6135 		  if (value->string.language != NULL)
   6136 		    n = (int)strlen(value->string.language);
   6137 		  else
   6138 		    n = 0;
   6139 
   6140 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
   6141 		  *bufptr++ = (ipp_uchar_t)n;
   6142 
   6143                  /* Language */
   6144 		  if (n > 0)
   6145 		  {
   6146 		    memcpy(bufptr, value->string.language, (size_t)n);
   6147 		    bufptr += n;
   6148 		  }
   6149 
   6150                  /* Length of text */
   6151                   if (value->string.text != NULL)
   6152 		    n = (int)strlen(value->string.text);
   6153 		  else
   6154 		    n = 0;
   6155 
   6156 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
   6157 		  *bufptr++ = (ipp_uchar_t)n;
   6158 
   6159                  /* Text */
   6160 		  if (n > 0)
   6161 		  {
   6162 		    memcpy(bufptr, value->string.text, (size_t)n);
   6163 		    bufptr += n;
   6164 		  }
   6165 		}
   6166 		break;
   6167 
   6168             case IPP_TAG_BEGIN_COLLECTION :
   6169 	        for (i = 0, value = attr->values;
   6170 		     i < attr->num_values;
   6171 		     i ++, value ++)
   6172 		{
   6173 		 /*
   6174 		  * Collections are written with the begin-collection
   6175 		  * tag first with a value of 0 length, followed by the
   6176 		  * attributes in the collection, then the end-collection
   6177 		  * value...
   6178 		  */
   6179 
   6180                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 5)
   6181 		  {
   6182                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6183 	            {
   6184 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   6185 		                 "attribute...");
   6186 		      _cupsBufferRelease((char *)buffer);
   6187 	              return (IPP_STATE_ERROR);
   6188 	            }
   6189 
   6190 		    bufptr = buffer;
   6191 		  }
   6192 
   6193 		  if (i)
   6194 		  {
   6195 		   /*
   6196 		    * Arrays and sets are done by sending additional
   6197 		    * values with a zero-length name...
   6198 		    */
   6199 
   6200                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   6201 		    *bufptr++ = 0;
   6202 		    *bufptr++ = 0;
   6203 		  }
   6204 
   6205                  /*
   6206 		  * Write a data length of 0 and flush the buffer...
   6207 		  */
   6208 
   6209 	          *bufptr++ = 0;
   6210 		  *bufptr++ = 0;
   6211 
   6212                   if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6213 	          {
   6214 	            DEBUG_puts("1ippWriteIO: Could not write IPP "
   6215 		               "attribute...");
   6216 		    _cupsBufferRelease((char *)buffer);
   6217 	            return (IPP_STATE_ERROR);
   6218 	          }
   6219 
   6220 		  bufptr = buffer;
   6221 
   6222                  /*
   6223 		  * Then write the collection attribute...
   6224 		  */
   6225 
   6226                   value->collection->state = IPP_STATE_IDLE;
   6227 
   6228 		  if (ippWriteIO(dst, cb, 1, ipp,
   6229 		                 value->collection) == IPP_STATE_ERROR)
   6230 		  {
   6231 		    DEBUG_puts("1ippWriteIO: Unable to write collection value");
   6232 		    _cupsBufferRelease((char *)buffer);
   6233 		    return (IPP_STATE_ERROR);
   6234 		  }
   6235 		}
   6236 		break;
   6237 
   6238             default :
   6239 	        for (i = 0, value = attr->values;
   6240 		     i < attr->num_values;
   6241 		     i ++, value ++)
   6242 		{
   6243 		  if (i)
   6244 		  {
   6245 		   /*
   6246 		    * Arrays and sets are done by sending additional
   6247 		    * values with a zero-length name...
   6248 		    */
   6249 
   6250                     if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
   6251 		    {
   6252                       if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6253 	              {
   6254 	        	DEBUG_puts("1ippWriteIO: Could not write IPP "
   6255 		                   "attribute...");
   6256 			_cupsBufferRelease((char *)buffer);
   6257 	        	return (IPP_STATE_ERROR);
   6258 	              }
   6259 
   6260 		      bufptr = buffer;
   6261 		    }
   6262 
   6263                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   6264 		    *bufptr++ = 0;
   6265 		    *bufptr++ = 0;
   6266 		  }
   6267 
   6268                  /*
   6269 		  * An unknown value might some new value that a
   6270 		  * vendor has come up with. It consists of a
   6271 		  * 2-byte length and the bytes in the unknown
   6272 		  * value buffer.
   6273 		  */
   6274 
   6275                   n = value->unknown.length;
   6276 
   6277                   if (n > (IPP_BUF_SIZE - 2))
   6278 		  {
   6279 		    DEBUG_printf(("1ippWriteIO: Data length too long (%d)",
   6280 		                  n));
   6281 		    _cupsBufferRelease((char *)buffer);
   6282 		    return (IPP_STATE_ERROR);
   6283 		  }
   6284 
   6285                   if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
   6286 		  {
   6287                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6288 	            {
   6289 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   6290 		                 "attribute...");
   6291 		      _cupsBufferRelease((char *)buffer);
   6292 	              return (IPP_STATE_ERROR);
   6293 	            }
   6294 
   6295 		    bufptr = buffer;
   6296 		  }
   6297 
   6298                  /* Length of unknown value */
   6299 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
   6300 		  *bufptr++ = (ipp_uchar_t)n;
   6301 
   6302                  /* Value */
   6303 		  if (n > 0)
   6304 		  {
   6305 		    memcpy(bufptr, value->unknown.data, (size_t)n);
   6306 		    bufptr += n;
   6307 		  }
   6308 		}
   6309 		break;
   6310 	  }
   6311 
   6312          /*
   6313 	  * Write the data out...
   6314 	  */
   6315 
   6316 	  if (bufptr > buffer)
   6317 	  {
   6318 	    if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6319 	    {
   6320 	      DEBUG_puts("1ippWriteIO: Could not write IPP attribute...");
   6321 	      _cupsBufferRelease((char *)buffer);
   6322 	      return (IPP_STATE_ERROR);
   6323 	    }
   6324 
   6325 	    DEBUG_printf(("2ippWriteIO: wrote %d bytes",
   6326 			  (int)(bufptr - buffer)));
   6327 	  }
   6328 
   6329 	 /*
   6330           * If blocking is disabled and we aren't at the end of the attribute
   6331           * list, stop here...
   6332 	  */
   6333 
   6334           if (!blocking && ipp->current)
   6335 	    break;
   6336 	}
   6337 
   6338 	if (ipp->current == NULL)
   6339 	{
   6340          /*
   6341 	  * Done with all of the attributes; add the end-of-attributes
   6342 	  * tag or end-collection attribute...
   6343 	  */
   6344 
   6345           if (parent == NULL)
   6346 	  {
   6347             buffer[0] = IPP_TAG_END;
   6348 	    n         = 1;
   6349 	  }
   6350 	  else
   6351 	  {
   6352             buffer[0] = IPP_TAG_END_COLLECTION;
   6353 	    buffer[1] = 0; /* empty name */
   6354 	    buffer[2] = 0;
   6355 	    buffer[3] = 0; /* empty value */
   6356 	    buffer[4] = 0;
   6357 	    n         = 5;
   6358 	  }
   6359 
   6360 	  if ((*cb)(dst, buffer, (size_t)n) < 0)
   6361 	  {
   6362 	    DEBUG_puts("1ippWriteIO: Could not write IPP end-tag...");
   6363 	    _cupsBufferRelease((char *)buffer);
   6364 	    return (IPP_STATE_ERROR);
   6365 	  }
   6366 
   6367 	  ipp->state = IPP_STATE_DATA;
   6368 	}
   6369         break;
   6370 
   6371     case IPP_STATE_DATA :
   6372         break;
   6373 
   6374     default :
   6375         break; /* anti-compiler-warning-code */
   6376   }
   6377 
   6378   _cupsBufferRelease((char *)buffer);
   6379 
   6380   return (ipp->state);
   6381 }
   6382 
   6383 
   6384 /*
   6385  * 'ipp_add_attr()' - Add a new attribute to the message.
   6386  */
   6387 
   6388 static ipp_attribute_t *		/* O - New attribute */
   6389 ipp_add_attr(ipp_t      *ipp,		/* I - IPP message */
   6390              const char *name,		/* I - Attribute name or NULL */
   6391              ipp_tag_t  group_tag,	/* I - Group tag or IPP_TAG_ZERO */
   6392              ipp_tag_t  value_tag,	/* I - Value tag or IPP_TAG_ZERO */
   6393              int        num_values)	/* I - Number of values */
   6394 {
   6395   int			alloc_values;	/* Number of values to allocate */
   6396   ipp_attribute_t	*attr;		/* New attribute */
   6397 
   6398 
   6399   DEBUG_printf(("4ipp_add_attr(ipp=%p, name=\"%s\", group_tag=0x%x, value_tag=0x%x, num_values=%d)", (void *)ipp, name, group_tag, value_tag, num_values));
   6400 
   6401  /*
   6402   * Range check input...
   6403   */
   6404 
   6405   if (!ipp || num_values < 0)
   6406     return (NULL);
   6407 
   6408  /*
   6409   * Allocate memory, rounding the allocation up as needed...
   6410   */
   6411 
   6412   if (num_values <= 1)
   6413     alloc_values = 1;
   6414   else
   6415     alloc_values = (num_values + IPP_MAX_VALUES - 1) & ~(IPP_MAX_VALUES - 1);
   6416 
   6417   attr = calloc(sizeof(ipp_attribute_t) +
   6418                 (size_t)(alloc_values - 1) * sizeof(_ipp_value_t), 1);
   6419 
   6420   if (attr)
   6421   {
   6422    /*
   6423     * Initialize attribute...
   6424     */
   6425 
   6426     DEBUG_printf(("4debug_alloc: %p %s %s%s (%d values)", (void *)attr, name, num_values > 1 ? "1setOf " : "", ippTagString(value_tag), num_values));
   6427 
   6428     if (name)
   6429       attr->name = _cupsStrAlloc(name);
   6430 
   6431     attr->group_tag  = group_tag;
   6432     attr->value_tag  = value_tag;
   6433     attr->num_values = num_values;
   6434 
   6435    /*
   6436     * Add it to the end of the linked list...
   6437     */
   6438 
   6439     if (ipp->last)
   6440       ipp->last->next = attr;
   6441     else
   6442       ipp->attrs = attr;
   6443 
   6444     ipp->prev = ipp->last;
   6445     ipp->last = ipp->current = attr;
   6446   }
   6447 
   6448   DEBUG_printf(("5ipp_add_attr: Returning %p", (void *)attr));
   6449 
   6450   return (attr);
   6451 }
   6452 
   6453 
   6454 /*
   6455  * 'ipp_free_values()' - Free attribute values.
   6456  */
   6457 
   6458 static void
   6459 ipp_free_values(ipp_attribute_t *attr,	/* I - Attribute to free values from */
   6460                 int             element,/* I - First value to free */
   6461                 int             count)	/* I - Number of values to free */
   6462 {
   6463   int		i;			/* Looping var */
   6464   _ipp_value_t	*value;			/* Current value */
   6465 
   6466 
   6467   DEBUG_printf(("4ipp_free_values(attr=%p, element=%d, count=%d)", (void *)attr, element, count));
   6468 
   6469   if (!(attr->value_tag & IPP_TAG_CUPS_CONST))
   6470   {
   6471    /*
   6472     * Free values as needed...
   6473     */
   6474 
   6475     switch (attr->value_tag)
   6476     {
   6477       case IPP_TAG_TEXTLANG :
   6478       case IPP_TAG_NAMELANG :
   6479 	  if (element == 0 && count == attr->num_values &&
   6480 	      attr->values[0].string.language)
   6481 	  {
   6482 	    _cupsStrFree(attr->values[0].string.language);
   6483 	    attr->values[0].string.language = NULL;
   6484 	  }
   6485 	  /* Fall through to other string values */
   6486 
   6487       case IPP_TAG_TEXT :
   6488       case IPP_TAG_NAME :
   6489       case IPP_TAG_RESERVED_STRING :
   6490       case IPP_TAG_KEYWORD :
   6491       case IPP_TAG_URI :
   6492       case IPP_TAG_URISCHEME :
   6493       case IPP_TAG_CHARSET :
   6494       case IPP_TAG_LANGUAGE :
   6495       case IPP_TAG_MIMETYPE :
   6496 	  for (i = count, value = attr->values + element;
   6497 	       i > 0;
   6498 	       i --, value ++)
   6499 	  {
   6500 	    _cupsStrFree(value->string.text);
   6501 	    value->string.text = NULL;
   6502 	  }
   6503 	  break;
   6504 
   6505       case IPP_TAG_DEFAULT :
   6506       case IPP_TAG_UNKNOWN :
   6507       case IPP_TAG_NOVALUE :
   6508       case IPP_TAG_NOTSETTABLE :
   6509       case IPP_TAG_DELETEATTR :
   6510       case IPP_TAG_ADMINDEFINE :
   6511       case IPP_TAG_INTEGER :
   6512       case IPP_TAG_ENUM :
   6513       case IPP_TAG_BOOLEAN :
   6514       case IPP_TAG_DATE :
   6515       case IPP_TAG_RESOLUTION :
   6516       case IPP_TAG_RANGE :
   6517 	  break;
   6518 
   6519       case IPP_TAG_BEGIN_COLLECTION :
   6520 	  for (i = count, value = attr->values + element;
   6521 	       i > 0;
   6522 	       i --, value ++)
   6523 	  {
   6524 	    ippDelete(value->collection);
   6525 	    value->collection = NULL;
   6526 	  }
   6527 	  break;
   6528 
   6529       case IPP_TAG_STRING :
   6530       default :
   6531 	  for (i = count, value = attr->values + element;
   6532 	       i > 0;
   6533 	       i --, value ++)
   6534 	  {
   6535 	    if (value->unknown.data)
   6536 	    {
   6537 	      free(value->unknown.data);
   6538 	      value->unknown.data = NULL;
   6539 	    }
   6540 	  }
   6541 	  break;
   6542     }
   6543   }
   6544 
   6545  /*
   6546   * If we are not freeing values from the end, move the remaining values up...
   6547   */
   6548 
   6549   if ((element + count) < attr->num_values)
   6550     memmove(attr->values + element, attr->values + element + count,
   6551             (size_t)(attr->num_values - count - element) * sizeof(_ipp_value_t));
   6552 
   6553   attr->num_values -= count;
   6554 }
   6555 
   6556 
   6557 /*
   6558  * 'ipp_get_code()' - Convert a C locale/charset name into an IPP language/charset code.
   6559  *
   6560  * This typically converts strings of the form "ll_CC", "ll-REGION", and "CHARSET_NUMBER"
   6561  * to "ll-cc", "ll-region", and "charset-number", respectively.
   6562  */
   6563 
   6564 static char *				/* O - Language code string */
   6565 ipp_get_code(const char *value,		/* I - Locale/charset string */
   6566              char       *buffer,	/* I - String buffer */
   6567              size_t     bufsize)	/* I - Size of string buffer */
   6568 {
   6569   char	*bufptr,			/* Pointer into buffer */
   6570 	*bufend;			/* End of buffer */
   6571 
   6572 
   6573  /*
   6574   * Convert values to lowercase and change _ to - as needed...
   6575   */
   6576 
   6577   for (bufptr = buffer, bufend = buffer + bufsize - 1;
   6578        *value && bufptr < bufend;
   6579        value ++)
   6580     if (*value == '_')
   6581       *bufptr++ = '-';
   6582     else
   6583       *bufptr++ = (char)_cups_tolower(*value);
   6584 
   6585   *bufptr = '\0';
   6586 
   6587  /*
   6588   * Return the converted string...
   6589   */
   6590 
   6591   return (buffer);
   6592 }
   6593 
   6594 
   6595 /*
   6596  * 'ipp_lang_code()' - Convert a C locale name into an IPP language code.
   6597  *
   6598  * This typically converts strings of the form "ll_CC" and "ll-REGION" to "ll-cc" and
   6599  * "ll-region", respectively.  It also converts the "C" (POSIX) locale to "en".
   6600  */
   6601 
   6602 static char *				/* O - Language code string */
   6603 ipp_lang_code(const char *locale,	/* I - Locale string */
   6604               char       *buffer,	/* I - String buffer */
   6605               size_t     bufsize)	/* I - Size of string buffer */
   6606 {
   6607  /*
   6608   * Map POSIX ("C") locale to generic English, otherwise convert the locale string as-is.
   6609   */
   6610 
   6611   if (!_cups_strcasecmp(locale, "c"))
   6612   {
   6613     strlcpy(buffer, "en", bufsize);
   6614     return (buffer);
   6615   }
   6616   else
   6617     return (ipp_get_code(locale, buffer, bufsize));
   6618 }
   6619 
   6620 
   6621 /*
   6622  * 'ipp_length()' - Compute the length of an IPP message or collection value.
   6623  */
   6624 
   6625 static size_t				/* O - Size of IPP message */
   6626 ipp_length(ipp_t *ipp,			/* I - IPP message or collection */
   6627            int   collection)		/* I - 1 if a collection, 0 otherwise */
   6628 {
   6629   int			i;		/* Looping var */
   6630   size_t		bytes;		/* Number of bytes */
   6631   ipp_attribute_t	*attr;		/* Current attribute */
   6632   ipp_tag_t		group;		/* Current group */
   6633   _ipp_value_t		*value;		/* Current value */
   6634 
   6635 
   6636   DEBUG_printf(("3ipp_length(ipp=%p, collection=%d)", (void *)ipp, collection));
   6637 
   6638   if (!ipp)
   6639   {
   6640     DEBUG_puts("4ipp_length: Returning 0 bytes");
   6641     return (0);
   6642   }
   6643 
   6644  /*
   6645   * Start with 8 bytes for the IPP message header...
   6646   */
   6647 
   6648   bytes = collection ? 0 : 8;
   6649 
   6650  /*
   6651   * Then add the lengths of each attribute...
   6652   */
   6653 
   6654   group = IPP_TAG_ZERO;
   6655 
   6656   for (attr = ipp->attrs; attr != NULL; attr = attr->next)
   6657   {
   6658     if (attr->group_tag != group && !collection)
   6659     {
   6660       group = attr->group_tag;
   6661       if (group == IPP_TAG_ZERO)
   6662 	continue;
   6663 
   6664       bytes ++;	/* Group tag */
   6665     }
   6666 
   6667     if (!attr->name)
   6668       continue;
   6669 
   6670     DEBUG_printf(("5ipp_length: attr->name=\"%s\", attr->num_values=%d, "
   6671                   "bytes=" CUPS_LLFMT, attr->name, attr->num_values, CUPS_LLCAST bytes));
   6672 
   6673     if ((attr->value_tag & ~IPP_TAG_CUPS_CONST) < IPP_TAG_EXTENSION)
   6674       bytes += (size_t)attr->num_values;/* Value tag for each value */
   6675     else
   6676       bytes += (size_t)(5 * attr->num_values);
   6677 					/* Value tag for each value */
   6678     bytes += (size_t)(2 * attr->num_values);
   6679 					/* Name lengths */
   6680     bytes += strlen(attr->name);	/* Name */
   6681     bytes += (size_t)(2 * attr->num_values);
   6682 					/* Value lengths */
   6683 
   6684     if (collection)
   6685       bytes += 5;			/* Add membername overhead */
   6686 
   6687     switch (attr->value_tag & ~IPP_TAG_CUPS_CONST)
   6688     {
   6689       case IPP_TAG_UNSUPPORTED_VALUE :
   6690       case IPP_TAG_DEFAULT :
   6691       case IPP_TAG_UNKNOWN :
   6692       case IPP_TAG_NOVALUE :
   6693       case IPP_TAG_NOTSETTABLE :
   6694       case IPP_TAG_DELETEATTR :
   6695       case IPP_TAG_ADMINDEFINE :
   6696           break;
   6697 
   6698       case IPP_TAG_INTEGER :
   6699       case IPP_TAG_ENUM :
   6700           bytes += (size_t)(4 * attr->num_values);
   6701 	  break;
   6702 
   6703       case IPP_TAG_BOOLEAN :
   6704           bytes += (size_t)attr->num_values;
   6705 	  break;
   6706 
   6707       case IPP_TAG_TEXT :
   6708       case IPP_TAG_NAME :
   6709       case IPP_TAG_KEYWORD :
   6710       case IPP_TAG_URI :
   6711       case IPP_TAG_URISCHEME :
   6712       case IPP_TAG_CHARSET :
   6713       case IPP_TAG_LANGUAGE :
   6714       case IPP_TAG_MIMETYPE :
   6715 	  for (i = 0, value = attr->values;
   6716 	       i < attr->num_values;
   6717 	       i ++, value ++)
   6718 	    if (value->string.text)
   6719 	      bytes += strlen(value->string.text);
   6720 	  break;
   6721 
   6722       case IPP_TAG_DATE :
   6723           bytes += (size_t)(11 * attr->num_values);
   6724 	  break;
   6725 
   6726       case IPP_TAG_RESOLUTION :
   6727           bytes += (size_t)(9 * attr->num_values);
   6728 	  break;
   6729 
   6730       case IPP_TAG_RANGE :
   6731           bytes += (size_t)(8 * attr->num_values);
   6732 	  break;
   6733 
   6734       case IPP_TAG_TEXTLANG :
   6735       case IPP_TAG_NAMELANG :
   6736           bytes += (size_t)(4 * attr->num_values);
   6737 					/* Charset + text length */
   6738 
   6739 	  for (i = 0, value = attr->values;
   6740 	       i < attr->num_values;
   6741 	       i ++, value ++)
   6742 	  {
   6743 	    if (value->string.language)
   6744 	      bytes += strlen(value->string.language);
   6745 
   6746 	    if (value->string.text)
   6747 	      bytes += strlen(value->string.text);
   6748 	  }
   6749 	  break;
   6750 
   6751       case IPP_TAG_BEGIN_COLLECTION :
   6752 	  for (i = 0, value = attr->values;
   6753 	       i < attr->num_values;
   6754 	       i ++, value ++)
   6755             bytes += ipp_length(value->collection, 1);
   6756 	  break;
   6757 
   6758       default :
   6759 	  for (i = 0, value = attr->values;
   6760 	       i < attr->num_values;
   6761 	       i ++, value ++)
   6762             bytes += (size_t)value->unknown.length;
   6763 	  break;
   6764     }
   6765   }
   6766 
   6767  /*
   6768   * Finally, add 1 byte for the "end of attributes" tag or 5 bytes
   6769   * for the "end of collection" tag and return...
   6770   */
   6771 
   6772   if (collection)
   6773     bytes += 5;
   6774   else
   6775     bytes ++;
   6776 
   6777   DEBUG_printf(("4ipp_length: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST bytes));
   6778 
   6779   return (bytes);
   6780 }
   6781 
   6782 
   6783 /*
   6784  * 'ipp_read_http()' - Semi-blocking read on a HTTP connection...
   6785  */
   6786 
   6787 static ssize_t				/* O - Number of bytes read */
   6788 ipp_read_http(http_t      *http,	/* I - Client connection */
   6789               ipp_uchar_t *buffer,	/* O - Buffer for data */
   6790 	      size_t      length)	/* I - Total length */
   6791 {
   6792   ssize_t	tbytes,			/* Total bytes read */
   6793 		bytes;			/* Bytes read this pass */
   6794 
   6795 
   6796   DEBUG_printf(("7ipp_read_http(http=%p, buffer=%p, length=%d)", (void *)http, (void *)buffer, (int)length));
   6797 
   6798  /*
   6799   * Loop until all bytes are read...
   6800   */
   6801 
   6802   for (tbytes = 0, bytes = 0;
   6803        tbytes < (int)length;
   6804        tbytes += bytes, buffer += bytes)
   6805   {
   6806     DEBUG_printf(("9ipp_read_http: tbytes=" CUPS_LLFMT ", http->state=%d", CUPS_LLCAST tbytes, http->state));
   6807 
   6808     if (http->state == HTTP_STATE_WAITING)
   6809       break;
   6810 
   6811     if (http->used == 0 && !http->blocking)
   6812     {
   6813      /*
   6814       * Wait up to 10 seconds for more data on non-blocking sockets...
   6815       */
   6816 
   6817       if (!httpWait(http, 10000))
   6818       {
   6819        /*
   6820 	* Signal no data...
   6821 	*/
   6822 
   6823 	bytes = -1;
   6824 	break;
   6825       }
   6826     }
   6827     else if (http->used == 0 && http->timeout_value > 0)
   6828     {
   6829      /*
   6830       * Wait up to timeout seconds for more data on blocking sockets...
   6831       */
   6832 
   6833       if (!httpWait(http, (int)(1000 * http->timeout_value)))
   6834       {
   6835        /*
   6836 	* Signal no data...
   6837 	*/
   6838 
   6839 	bytes = -1;
   6840 	break;
   6841       }
   6842     }
   6843 
   6844     if ((bytes = httpRead2(http, (char *)buffer, length - (size_t)tbytes)) < 0)
   6845     {
   6846 #ifdef WIN32
   6847       break;
   6848 #else
   6849       if (errno != EAGAIN && errno != EINTR)
   6850 	break;
   6851 
   6852       bytes = 0;
   6853 #endif /* WIN32 */
   6854     }
   6855     else if (bytes == 0)
   6856       break;
   6857   }
   6858 
   6859  /*
   6860   * Return the number of bytes read...
   6861   */
   6862 
   6863   if (tbytes == 0 && bytes < 0)
   6864     tbytes = -1;
   6865 
   6866   DEBUG_printf(("8ipp_read_http: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST tbytes));
   6867 
   6868   return (tbytes);
   6869 }
   6870 
   6871 
   6872 /*
   6873  * 'ipp_read_file()' - Read IPP data from a file.
   6874  */
   6875 
   6876 static ssize_t				/* O - Number of bytes read */
   6877 ipp_read_file(int         *fd,		/* I - File descriptor */
   6878               ipp_uchar_t *buffer,	/* O - Read buffer */
   6879 	      size_t      length)	/* I - Number of bytes to read */
   6880 {
   6881 #ifdef WIN32
   6882   return ((ssize_t)read(*fd, buffer, (unsigned)length));
   6883 #else
   6884   return (read(*fd, buffer, length));
   6885 #endif /* WIN32 */
   6886 }
   6887 
   6888 
   6889 /*
   6890  * 'ipp_set_error()' - Set a formatted, localized error string.
   6891  */
   6892 
   6893 static void
   6894 ipp_set_error(ipp_status_t status,	/* I - Status code */
   6895               const char   *format,	/* I - Printf-style error string */
   6896 	      ...)			/* I - Additional arguments as needed */
   6897 {
   6898   va_list	ap;			/* Pointer to additional args */
   6899   char		buffer[2048];		/* Message buffer */
   6900   cups_lang_t	*lang = cupsLangDefault();
   6901 					/* Current language */
   6902 
   6903 
   6904   va_start(ap, format);
   6905   vsnprintf(buffer, sizeof(buffer), _cupsLangString(lang, format), ap);
   6906   va_end(ap);
   6907 
   6908   _cupsSetError(status, buffer, 0);
   6909 }
   6910 
   6911 
   6912 /*
   6913  * 'ipp_set_value()' - Get the value element from an attribute, expanding it as
   6914  *                     needed.
   6915  */
   6916 
   6917 static _ipp_value_t *			/* O  - IPP value element or NULL on error */
   6918 ipp_set_value(ipp_t           *ipp,	/* IO - IPP message */
   6919               ipp_attribute_t **attr,	/* IO - IPP attribute */
   6920               int             element)	/* I  - Value number (0-based) */
   6921 {
   6922   ipp_attribute_t	*temp,		/* New attribute pointer */
   6923 			*current,	/* Current attribute in list */
   6924 			*prev;		/* Previous attribute in list */
   6925   int			alloc_values;	/* Allocated values */
   6926 
   6927 
   6928  /*
   6929   * If we are setting an existing value element, return it...
   6930   */
   6931 
   6932   temp = *attr;
   6933 
   6934   if (temp->num_values <= 1)
   6935     alloc_values = 1;
   6936   else
   6937     alloc_values = (temp->num_values + IPP_MAX_VALUES - 1) &
   6938                    ~(IPP_MAX_VALUES - 1);
   6939 
   6940   if (element < alloc_values)
   6941   {
   6942     if (element >= temp->num_values)
   6943       temp->num_values = element + 1;
   6944 
   6945     return (temp->values + element);
   6946   }
   6947 
   6948  /*
   6949   * Otherwise re-allocate the attribute - we allocate in groups of IPP_MAX_VALUE
   6950   * values when num_values > 1.
   6951   */
   6952 
   6953   if (alloc_values < IPP_MAX_VALUES)
   6954     alloc_values = IPP_MAX_VALUES;
   6955   else
   6956     alloc_values += IPP_MAX_VALUES;
   6957 
   6958   DEBUG_printf(("4ipp_set_value: Reallocating for up to %d values.",
   6959                 alloc_values));
   6960 
   6961  /*
   6962   * Reallocate memory...
   6963   */
   6964 
   6965   if ((temp = realloc(temp, sizeof(ipp_attribute_t) + (size_t)(alloc_values - 1) * sizeof(_ipp_value_t))) == NULL)
   6966   {
   6967     _cupsSetHTTPError(HTTP_STATUS_ERROR);
   6968     DEBUG_puts("4ipp_set_value: Unable to resize attribute.");
   6969     return (NULL);
   6970   }
   6971 
   6972  /*
   6973   * Zero the new memory...
   6974   */
   6975 
   6976   memset(temp->values + temp->num_values, 0, (size_t)(alloc_values - temp->num_values) * sizeof(_ipp_value_t));
   6977 
   6978   if (temp != *attr)
   6979   {
   6980    /*
   6981     * Reset pointers in the list...
   6982     */
   6983 
   6984     DEBUG_printf(("4debug_free: %p %s", (void *)*attr, temp->name));
   6985     DEBUG_printf(("4debug_alloc: %p %s %s%s (%d)", (void *)temp, temp->name, temp->num_values > 1 ? "1setOf " : "", ippTagString(temp->value_tag), temp->num_values));
   6986 
   6987     if (ipp->current == *attr && ipp->prev)
   6988     {
   6989      /*
   6990       * Use current "previous" pointer...
   6991       */
   6992 
   6993       prev = ipp->prev;
   6994     }
   6995     else
   6996     {
   6997      /*
   6998       * Find this attribute in the linked list...
   6999       */
   7000 
   7001       for (prev = NULL, current = ipp->attrs;
   7002 	   current && current != *attr;
   7003 	   prev = current, current = current->next);
   7004 
   7005       if (!current)
   7006       {
   7007        /*
   7008 	* This is a serious error!
   7009 	*/
   7010 
   7011 	*attr = temp;
   7012 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   7013 	              _("IPP attribute is not a member of the message."), 1);
   7014 	DEBUG_puts("4ipp_set_value: Unable to find attribute in message.");
   7015 	return (NULL);
   7016       }
   7017     }
   7018 
   7019     if (prev)
   7020       prev->next = temp;
   7021     else
   7022       ipp->attrs = temp;
   7023 
   7024     ipp->current = temp;
   7025     ipp->prev    = prev;
   7026 
   7027     if (ipp->last == *attr)
   7028       ipp->last = temp;
   7029 
   7030     *attr = temp;
   7031   }
   7032 
   7033  /*
   7034   * Return the value element...
   7035   */
   7036 
   7037   if (element >= temp->num_values)
   7038     temp->num_values = element + 1;
   7039 
   7040   return (temp->values + element);
   7041 }
   7042 
   7043 
   7044 /*
   7045  * 'ipp_write_file()' - Write IPP data to a file.
   7046  */
   7047 
   7048 static ssize_t				/* O - Number of bytes written */
   7049 ipp_write_file(int         *fd,		/* I - File descriptor */
   7050                ipp_uchar_t *buffer,	/* I - Data to write */
   7051                size_t      length)	/* I - Number of bytes to write */
   7052 {
   7053 #ifdef WIN32
   7054   return ((ssize_t)write(*fd, buffer, (unsigned)length));
   7055 #else
   7056   return (write(*fd, buffer, length));
   7057 #endif /* WIN32 */
   7058 }
   7059