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 date 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  * language, mimeMediaType, name, text, URI, or URI scheme 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_INTEGER :
   1513     case IPP_TAG_ENUM :
   1514         dstattr = ippAddIntegers(dst, srcattr->group_tag, srcattr->value_tag,
   1515 	                         srcattr->name, srcattr->num_values, NULL);
   1516         if (!dstattr)
   1517           break;
   1518 
   1519         for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
   1520              i > 0;
   1521              i --, srcval ++, dstval ++)
   1522 	  dstval->integer = srcval->integer;
   1523         break;
   1524 
   1525     case IPP_TAG_BOOLEAN :
   1526         dstattr = ippAddBooleans(dst, srcattr->group_tag, srcattr->name,
   1527 	                        srcattr->num_values, NULL);
   1528         if (!dstattr)
   1529           break;
   1530 
   1531         for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
   1532              i > 0;
   1533              i --, srcval ++, dstval ++)
   1534 	  dstval->boolean = srcval->boolean;
   1535         break;
   1536 
   1537     case IPP_TAG_TEXT :
   1538     case IPP_TAG_NAME :
   1539     case IPP_TAG_KEYWORD :
   1540     case IPP_TAG_URI :
   1541     case IPP_TAG_URISCHEME :
   1542     case IPP_TAG_CHARSET :
   1543     case IPP_TAG_LANGUAGE :
   1544     case IPP_TAG_MIMETYPE :
   1545         dstattr = ippAddStrings(dst, srcattr->group_tag,
   1546 	                        (ipp_tag_t)(srcattr->value_tag | quickcopy),
   1547 	                        srcattr->name, srcattr->num_values, NULL, NULL);
   1548         if (!dstattr)
   1549           break;
   1550 
   1551         if (quickcopy)
   1552 	{
   1553 	  for (i = srcattr->num_values, srcval = srcattr->values,
   1554 	           dstval = dstattr->values;
   1555 	       i > 0;
   1556 	       i --, srcval ++, dstval ++)
   1557 	    dstval->string.text = srcval->string.text;
   1558         }
   1559 	else if (srcattr->value_tag & IPP_TAG_CUPS_CONST)
   1560 	{
   1561 	  for (i = srcattr->num_values, srcval = srcattr->values,
   1562 	           dstval = dstattr->values;
   1563 	       i > 0;
   1564 	       i --, srcval ++, dstval ++)
   1565 	    dstval->string.text = _cupsStrAlloc(srcval->string.text);
   1566 	}
   1567 	else
   1568 	{
   1569 	  for (i = srcattr->num_values, srcval = srcattr->values,
   1570 	           dstval = dstattr->values;
   1571 	       i > 0;
   1572 	       i --, srcval ++, dstval ++)
   1573 	    dstval->string.text = _cupsStrRetain(srcval->string.text);
   1574 	}
   1575         break;
   1576 
   1577     case IPP_TAG_DATE :
   1578         if (srcattr->num_values != 1)
   1579           return (NULL);
   1580 
   1581         dstattr = ippAddDate(dst, srcattr->group_tag, srcattr->name,
   1582 	                     srcattr->values[0].date);
   1583         break;
   1584 
   1585     case IPP_TAG_RESOLUTION :
   1586         dstattr = ippAddResolutions(dst, srcattr->group_tag, srcattr->name,
   1587 	                            srcattr->num_values, IPP_RES_PER_INCH,
   1588 				    NULL, NULL);
   1589         if (!dstattr)
   1590           break;
   1591 
   1592         for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
   1593              i > 0;
   1594              i --, srcval ++, dstval ++)
   1595 	{
   1596 	  dstval->resolution.xres  = srcval->resolution.xres;
   1597 	  dstval->resolution.yres  = srcval->resolution.yres;
   1598 	  dstval->resolution.units = srcval->resolution.units;
   1599 	}
   1600         break;
   1601 
   1602     case IPP_TAG_RANGE :
   1603         dstattr = ippAddRanges(dst, srcattr->group_tag, srcattr->name,
   1604 	                       srcattr->num_values, NULL, NULL);
   1605         if (!dstattr)
   1606           break;
   1607 
   1608         for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
   1609              i > 0;
   1610              i --, srcval ++, dstval ++)
   1611 	{
   1612 	  dstval->range.lower = srcval->range.lower;
   1613 	  dstval->range.upper = srcval->range.upper;
   1614 	}
   1615         break;
   1616 
   1617     case IPP_TAG_TEXTLANG :
   1618     case IPP_TAG_NAMELANG :
   1619         dstattr = ippAddStrings(dst, srcattr->group_tag,
   1620 	                        (ipp_tag_t)(srcattr->value_tag | quickcopy),
   1621 	                        srcattr->name, srcattr->num_values, NULL, NULL);
   1622         if (!dstattr)
   1623           break;
   1624 
   1625         if (quickcopy)
   1626 	{
   1627 	  for (i = srcattr->num_values, srcval = srcattr->values,
   1628 	           dstval = dstattr->values;
   1629 	       i > 0;
   1630 	       i --, srcval ++, dstval ++)
   1631 	  {
   1632             dstval->string.language = srcval->string.language;
   1633 	    dstval->string.text     = srcval->string.text;
   1634           }
   1635         }
   1636 	else if (srcattr->value_tag & IPP_TAG_CUPS_CONST)
   1637 	{
   1638 	  for (i = srcattr->num_values, srcval = srcattr->values,
   1639 	           dstval = dstattr->values;
   1640 	       i > 0;
   1641 	       i --, srcval ++, dstval ++)
   1642 	  {
   1643 	    if (srcval == srcattr->values)
   1644               dstval->string.language = _cupsStrAlloc(srcval->string.language);
   1645 	    else
   1646               dstval->string.language = dstattr->values[0].string.language;
   1647 
   1648 	    dstval->string.text = _cupsStrAlloc(srcval->string.text);
   1649           }
   1650         }
   1651 	else
   1652 	{
   1653 	  for (i = srcattr->num_values, srcval = srcattr->values,
   1654 	           dstval = dstattr->values;
   1655 	       i > 0;
   1656 	       i --, srcval ++, dstval ++)
   1657 	  {
   1658 	    if (srcval == srcattr->values)
   1659               dstval->string.language = _cupsStrRetain(srcval->string.language);
   1660 	    else
   1661               dstval->string.language = dstattr->values[0].string.language;
   1662 
   1663 	    dstval->string.text = _cupsStrRetain(srcval->string.text);
   1664           }
   1665         }
   1666         break;
   1667 
   1668     case IPP_TAG_BEGIN_COLLECTION :
   1669         dstattr = ippAddCollections(dst, srcattr->group_tag, srcattr->name,
   1670 	                            srcattr->num_values, NULL);
   1671         if (!dstattr)
   1672           break;
   1673 
   1674         for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
   1675              i > 0;
   1676              i --, srcval ++, dstval ++)
   1677 	{
   1678 	  dstval->collection = srcval->collection;
   1679 	  srcval->collection->use ++;
   1680 	}
   1681         break;
   1682 
   1683     case IPP_TAG_STRING :
   1684     default :
   1685         /* TODO: Implement quick copy for unknown/octetString values */
   1686         dstattr = ippAddIntegers(dst, srcattr->group_tag, srcattr->value_tag,
   1687 	                         srcattr->name, srcattr->num_values, NULL);
   1688         if (!dstattr)
   1689           break;
   1690 
   1691         for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
   1692              i > 0;
   1693              i --, srcval ++, dstval ++)
   1694 	{
   1695 	  dstval->unknown.length = srcval->unknown.length;
   1696 
   1697 	  if (dstval->unknown.length > 0)
   1698 	  {
   1699 	    if ((dstval->unknown.data = malloc((size_t)dstval->unknown.length)) == NULL)
   1700 	      dstval->unknown.length = 0;
   1701 	    else
   1702 	      memcpy(dstval->unknown.data, srcval->unknown.data, (size_t)dstval->unknown.length);
   1703 	  }
   1704 	}
   1705         break; /* anti-compiler-warning-code */
   1706   }
   1707 
   1708   return (dstattr);
   1709 }
   1710 
   1711 
   1712 /*
   1713  * 'ippCopyAttributes()' - Copy attributes from one IPP message to another.
   1714  *
   1715  * Zero or more attributes are copied from the source IPP message, @code src@, to the
   1716  * destination IPP message, @code dst@. When @code quickcopy@ is non-zero, a "shallow"
   1717  * reference copy of the attribute is created - this should only be done as long as the
   1718  * original source IPP message will not be freed for the life of the destination.
   1719  *
   1720  * The @code cb@ and @code context@ parameters provide a generic way to "filter" the
   1721  * attributes that are copied - the function must return 1 to copy the attribute or
   1722  * 0 to skip it. The function may also choose to do a partial copy of the source attribute
   1723  * itself.
   1724  *
   1725  * @since CUPS 1.6/macOS 10.8@
   1726  */
   1727 
   1728 int					/* O - 1 on success, 0 on error */
   1729 ippCopyAttributes(
   1730     ipp_t        *dst,			/* I - Destination IPP message */
   1731     ipp_t        *src,			/* I - Source IPP message */
   1732     int          quickcopy,		/* I - 1 for a referenced copy, 0 for normal */
   1733     ipp_copycb_t cb,			/* I - Copy callback or @code NULL@ for none */
   1734     void         *context)		/* I - Context pointer */
   1735 {
   1736   ipp_attribute_t	*srcattr;	/* Source attribute */
   1737 
   1738 
   1739   DEBUG_printf(("ippCopyAttributes(dst=%p, src=%p, quickcopy=%d, cb=%p, context=%p)", (void *)dst, (void *)src, quickcopy, (void *)cb, context));
   1740 
   1741  /*
   1742   * Range check input...
   1743   */
   1744 
   1745   if (!dst || !src)
   1746     return (0);
   1747 
   1748  /*
   1749   * Loop through source attributes and copy as needed...
   1750   */
   1751 
   1752   for (srcattr = src->attrs; srcattr; srcattr = srcattr->next)
   1753     if (!cb || (*cb)(context, dst, srcattr))
   1754       if (!ippCopyAttribute(dst, srcattr, quickcopy))
   1755         return (0);
   1756 
   1757   return (1);
   1758 }
   1759 
   1760 
   1761 /*
   1762  * 'ippDateToTime()' - Convert from RFC 1903 Date/Time format to UNIX time
   1763  *                     in seconds.
   1764  */
   1765 
   1766 time_t					/* O - UNIX time value */
   1767 ippDateToTime(const ipp_uchar_t *date)	/* I - RFC 1903 date info */
   1768 {
   1769   struct tm	unixdate;		/* UNIX date/time info */
   1770   time_t	t;			/* Computed time */
   1771 
   1772 
   1773   if (!date)
   1774     return (0);
   1775 
   1776   memset(&unixdate, 0, sizeof(unixdate));
   1777 
   1778  /*
   1779   * RFC-1903 date/time format is:
   1780   *
   1781   *    Byte(s)  Description
   1782   *    -------  -----------
   1783   *    0-1      Year (0 to 65535)
   1784   *    2        Month (1 to 12)
   1785   *    3        Day (1 to 31)
   1786   *    4        Hours (0 to 23)
   1787   *    5        Minutes (0 to 59)
   1788   *    6        Seconds (0 to 60, 60 = "leap second")
   1789   *    7        Deciseconds (0 to 9)
   1790   *    8        +/- UTC
   1791   *    9        UTC hours (0 to 11)
   1792   *    10       UTC minutes (0 to 59)
   1793   */
   1794 
   1795   unixdate.tm_year = ((date[0] << 8) | date[1]) - 1900;
   1796   unixdate.tm_mon  = date[2] - 1;
   1797   unixdate.tm_mday = date[3];
   1798   unixdate.tm_hour = date[4];
   1799   unixdate.tm_min  = date[5];
   1800   unixdate.tm_sec  = date[6];
   1801 
   1802   t = mktime(&unixdate);
   1803 
   1804   if (date[8] == '-')
   1805     t += date[9] * 3600 + date[10] * 60;
   1806   else
   1807     t -= date[9] * 3600 + date[10] * 60;
   1808 
   1809   return (t);
   1810 }
   1811 
   1812 
   1813 /*
   1814  * 'ippDelete()' - Delete an IPP message.
   1815  */
   1816 
   1817 void
   1818 ippDelete(ipp_t *ipp)			/* I - IPP message */
   1819 {
   1820   ipp_attribute_t	*attr,		/* Current attribute */
   1821 			*next;		/* Next attribute */
   1822 
   1823 
   1824   DEBUG_printf(("ippDelete(ipp=%p)", (void *)ipp));
   1825 
   1826   if (!ipp)
   1827     return;
   1828 
   1829   ipp->use --;
   1830   if (ipp->use > 0)
   1831     return;
   1832 
   1833   for (attr = ipp->attrs; attr != NULL; attr = next)
   1834   {
   1835     next = attr->next;
   1836 
   1837     ipp_free_values(attr, 0, attr->num_values);
   1838 
   1839     if (attr->name)
   1840       _cupsStrFree(attr->name);
   1841 
   1842     free(attr);
   1843   }
   1844 
   1845   free(ipp);
   1846 }
   1847 
   1848 
   1849 /*
   1850  * 'ippDeleteAttribute()' - Delete a single attribute in an IPP message.
   1851  *
   1852  * @since CUPS 1.1.19/macOS 10.3@
   1853  */
   1854 
   1855 void
   1856 ippDeleteAttribute(
   1857     ipp_t           *ipp,		/* I - IPP message */
   1858     ipp_attribute_t *attr)		/* I - Attribute to delete */
   1859 {
   1860   ipp_attribute_t	*current,	/* Current attribute */
   1861 			*prev;		/* Previous attribute */
   1862 
   1863 
   1864   DEBUG_printf(("ippDeleteAttribute(ipp=%p, attr=%p(%s))", (void *)ipp, (void *)attr, attr ? attr->name : "(null)"));
   1865 
   1866  /*
   1867   * Range check input...
   1868   */
   1869 
   1870   if (!attr)
   1871     return;
   1872 
   1873  /*
   1874   * Find the attribute in the list...
   1875   */
   1876 
   1877   if (ipp)
   1878   {
   1879     for (current = ipp->attrs, prev = NULL;
   1880 	 current;
   1881 	 prev = current, current = current->next)
   1882       if (current == attr)
   1883       {
   1884        /*
   1885 	* Found it, remove the attribute from the list...
   1886 	*/
   1887 
   1888 	if (prev)
   1889 	  prev->next = current->next;
   1890 	else
   1891 	  ipp->attrs = current->next;
   1892 
   1893 	if (current == ipp->last)
   1894 	  ipp->last = prev;
   1895 
   1896         break;
   1897       }
   1898 
   1899     if (!current)
   1900       return;
   1901   }
   1902 
   1903  /*
   1904   * Free memory used by the attribute...
   1905   */
   1906 
   1907   ipp_free_values(attr, 0, attr->num_values);
   1908 
   1909   if (attr->name)
   1910     _cupsStrFree(attr->name);
   1911 
   1912   free(attr);
   1913 }
   1914 
   1915 
   1916 /*
   1917  * 'ippDeleteValues()' - Delete values in an attribute.
   1918  *
   1919  * The @code element@ parameter specifies the first value to delete, starting at
   1920  * 0. It must be less than the number of values returned by @link ippGetCount@.
   1921  *
   1922  * The @code attr@ parameter may be modified as a result of setting the value.
   1923  *
   1924  * Deleting all values in an attribute deletes the attribute.
   1925  *
   1926  * @since CUPS 1.6/macOS 10.8@
   1927  */
   1928 
   1929 int					/* O  - 1 on success, 0 on failure */
   1930 ippDeleteValues(
   1931     ipp_t           *ipp,		/* I  - IPP message */
   1932     ipp_attribute_t **attr,		/* IO - Attribute */
   1933     int             element,		/* I  - Index of first value to delete (0-based) */
   1934     int             count)		/* I  - Number of values to delete */
   1935 {
   1936  /*
   1937   * Range check input...
   1938   */
   1939 
   1940   if (!ipp || !attr || !*attr ||
   1941       element < 0 || element >= (*attr)->num_values || count <= 0 ||
   1942       (element + count) >= (*attr)->num_values)
   1943     return (0);
   1944 
   1945  /*
   1946   * If we are deleting all values, just delete the attribute entirely.
   1947   */
   1948 
   1949   if (count == (*attr)->num_values)
   1950   {
   1951     ippDeleteAttribute(ipp, *attr);
   1952     *attr = NULL;
   1953     return (1);
   1954   }
   1955 
   1956  /*
   1957   * Otherwise free the values in question and return.
   1958   */
   1959 
   1960   ipp_free_values(*attr, element, count);
   1961 
   1962   return (1);
   1963 }
   1964 
   1965 
   1966 /*
   1967  * 'ippFindAttribute()' - Find a named attribute in a request.
   1968  *
   1969  * Starting with CUPS 2.0, the attribute name can contain a hierarchical list
   1970  * of attribute and member names separated by slashes, for example
   1971  * "media-col/media-size".
   1972  */
   1973 
   1974 ipp_attribute_t	*			/* O - Matching attribute */
   1975 ippFindAttribute(ipp_t      *ipp,	/* I - IPP message */
   1976                  const char *name,	/* I - Name of attribute */
   1977 		 ipp_tag_t  type)	/* I - Type of attribute */
   1978 {
   1979   DEBUG_printf(("2ippFindAttribute(ipp=%p, name=\"%s\", type=%02x(%s))", (void *)ipp, name, type, ippTagString(type)));
   1980 
   1981   if (!ipp || !name)
   1982     return (NULL);
   1983 
   1984  /*
   1985   * Reset the current pointer...
   1986   */
   1987 
   1988   ipp->current = NULL;
   1989   ipp->atend   = 0;
   1990 
   1991  /*
   1992   * Search for the attribute...
   1993   */
   1994 
   1995   return (ippFindNextAttribute(ipp, name, type));
   1996 }
   1997 
   1998 
   1999 /*
   2000  * 'ippFindNextAttribute()' - Find the next named attribute in a request.
   2001  *
   2002  * Starting with CUPS 2.0, the attribute name can contain a hierarchical list
   2003  * of attribute and member names separated by slashes, for example
   2004  * "media-col/media-size".
   2005  */
   2006 
   2007 ipp_attribute_t	*			/* O - Matching attribute */
   2008 ippFindNextAttribute(ipp_t      *ipp,	/* I - IPP message */
   2009                      const char *name,	/* I - Name of attribute */
   2010 		     ipp_tag_t  type)	/* I - Type of attribute */
   2011 {
   2012   ipp_attribute_t	*attr,		/* Current atttribute */
   2013 			*childattr;	/* Child attribute */
   2014   ipp_tag_t		value_tag;	/* Value tag */
   2015   char			parent[1024],	/* Parent attribute name */
   2016 			*child = NULL;	/* Child attribute name */
   2017 
   2018 
   2019   DEBUG_printf(("2ippFindNextAttribute(ipp=%p, name=\"%s\", type=%02x(%s))", (void *)ipp, name, type, ippTagString(type)));
   2020 
   2021   if (!ipp || !name)
   2022     return (NULL);
   2023 
   2024   DEBUG_printf(("3ippFindNextAttribute: atend=%d", ipp->atend));
   2025 
   2026   if (ipp->atend)
   2027     return (NULL);
   2028 
   2029   if (strchr(name, '/'))
   2030   {
   2031    /*
   2032     * Search for child attribute...
   2033     */
   2034 
   2035     strlcpy(parent, name, sizeof(parent));
   2036     if ((child = strchr(parent, '/')) == NULL)
   2037     {
   2038       DEBUG_puts("3ippFindNextAttribute: Attribute name too long.");
   2039       return (NULL);
   2040     }
   2041 
   2042     *child++ = '\0';
   2043 
   2044     if (ipp->current && ipp->current->name && ipp->current->value_tag == IPP_TAG_BEGIN_COLLECTION && !strcmp(parent, ipp->current->name))
   2045     {
   2046       while (ipp->curindex < ipp->current->num_values)
   2047       {
   2048         if ((childattr = ippFindNextAttribute(ipp->current->values[ipp->curindex].collection, child, type)) != NULL)
   2049           return (childattr);
   2050 
   2051         ipp->curindex ++;
   2052         if (ipp->curindex < ipp->current->num_values && ipp->current->values[ipp->curindex].collection)
   2053           ipp->current->values[ipp->curindex].collection->current = NULL;
   2054       }
   2055 
   2056       ipp->prev     = ipp->current;
   2057       ipp->current  = ipp->current->next;
   2058       ipp->curindex = 0;
   2059 
   2060       if (!ipp->current)
   2061       {
   2062         ipp->atend = 1;
   2063         return (NULL);
   2064       }
   2065     }
   2066 
   2067     if (!ipp->current)
   2068     {
   2069       ipp->prev     = NULL;
   2070       ipp->current  = ipp->attrs;
   2071       ipp->curindex = 0;
   2072     }
   2073 
   2074     name = parent;
   2075     attr = ipp->current;
   2076   }
   2077   else if (ipp->current)
   2078   {
   2079     ipp->prev = ipp->current;
   2080     attr      = ipp->current->next;
   2081   }
   2082   else
   2083   {
   2084     ipp->prev = NULL;
   2085     attr      = ipp->attrs;
   2086   }
   2087 
   2088   for (; attr != NULL; ipp->prev = attr, attr = attr->next)
   2089   {
   2090     DEBUG_printf(("4ippFindAttribute: attr=%p, name=\"%s\"", (void *)attr, attr->name));
   2091 
   2092     value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_CUPS_MASK);
   2093 
   2094     if (attr->name != NULL && _cups_strcasecmp(attr->name, name) == 0 &&
   2095         (value_tag == type || type == IPP_TAG_ZERO || name == parent ||
   2096 	 (value_tag == IPP_TAG_TEXTLANG && type == IPP_TAG_TEXT) ||
   2097 	 (value_tag == IPP_TAG_NAMELANG && type == IPP_TAG_NAME)))
   2098     {
   2099       ipp->current = attr;
   2100 
   2101       if (name == parent && attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
   2102       {
   2103         int i;				/* Looping var */
   2104 
   2105         for (i = 0; i < attr->num_values; i ++)
   2106         {
   2107 	  if ((childattr = ippFindAttribute(attr->values[i].collection, child, type)) != NULL)
   2108 	  {
   2109 	    attr->values[0].collection->curindex = i;
   2110 	    return (childattr);
   2111 	  }
   2112         }
   2113       }
   2114       else
   2115         return (attr);
   2116     }
   2117   }
   2118 
   2119   ipp->current = NULL;
   2120   ipp->prev    = NULL;
   2121   ipp->atend   = 1;
   2122 
   2123   return (NULL);
   2124 }
   2125 
   2126 
   2127 /*
   2128  * 'ippFirstAttribute()' - Return the first attribute in the message.
   2129  *
   2130  * @since CUPS 1.6/macOS 10.8@
   2131  */
   2132 
   2133 ipp_attribute_t	*			/* O - First attribute or @code NULL@ if none */
   2134 ippFirstAttribute(ipp_t *ipp)		/* I - IPP message */
   2135 {
   2136  /*
   2137   * Range check input...
   2138   */
   2139 
   2140   if (!ipp)
   2141     return (NULL);
   2142 
   2143  /*
   2144   * Return the first attribute...
   2145   */
   2146 
   2147   return (ipp->current = ipp->attrs);
   2148 }
   2149 
   2150 
   2151 /*
   2152  * 'ippGetBoolean()' - Get a boolean value for an attribute.
   2153  *
   2154  * The @code element@ parameter specifies which value to get from 0 to
   2155  * @link ippGetCount(attr)@ - 1.
   2156  *
   2157  * @since CUPS 1.6/macOS 10.8@
   2158  */
   2159 
   2160 int					/* O - Boolean value or 0 on error */
   2161 ippGetBoolean(ipp_attribute_t *attr,	/* I - IPP attribute */
   2162               int             element)	/* I - Value number (0-based) */
   2163 {
   2164  /*
   2165   * Range check input...
   2166   */
   2167 
   2168   if (!attr || attr->value_tag != IPP_TAG_BOOLEAN ||
   2169       element < 0 || element >= attr->num_values)
   2170     return (0);
   2171 
   2172  /*
   2173   * Return the value...
   2174   */
   2175 
   2176   return (attr->values[element].boolean);
   2177 }
   2178 
   2179 
   2180 /*
   2181  * 'ippGetCollection()' - Get a collection value for an attribute.
   2182  *
   2183  * The @code element@ parameter specifies which value to get from 0 to
   2184  * @link ippGetCount(attr)@ - 1.
   2185  *
   2186  * @since CUPS 1.6/macOS 10.8@
   2187  */
   2188 
   2189 ipp_t *					/* O - Collection value or @code NULL@ on error */
   2190 ippGetCollection(
   2191     ipp_attribute_t *attr,		/* I - IPP attribute */
   2192     int             element)		/* I - Value number (0-based) */
   2193 {
   2194  /*
   2195   * Range check input...
   2196   */
   2197 
   2198   if (!attr || attr->value_tag != IPP_TAG_BEGIN_COLLECTION ||
   2199       element < 0 || element >= attr->num_values)
   2200     return (NULL);
   2201 
   2202  /*
   2203   * Return the value...
   2204   */
   2205 
   2206   return (attr->values[element].collection);
   2207 }
   2208 
   2209 
   2210 /*
   2211  * 'ippGetCount()' - Get the number of values in an attribute.
   2212  *
   2213  * @since CUPS 1.6/macOS 10.8@
   2214  */
   2215 
   2216 int					/* O - Number of values or 0 on error */
   2217 ippGetCount(ipp_attribute_t *attr)	/* I - IPP attribute */
   2218 {
   2219  /*
   2220   * Range check input...
   2221   */
   2222 
   2223   if (!attr)
   2224     return (0);
   2225 
   2226  /*
   2227   * Return the number of values...
   2228   */
   2229 
   2230   return (attr->num_values);
   2231 }
   2232 
   2233 
   2234 /*
   2235  * 'ippGetDate()' - Get a date value for an attribute.
   2236  *
   2237  * The @code element@ parameter specifies which value to get from 0 to
   2238  * @link ippGetCount(attr)@ - 1.
   2239  *
   2240  * @since CUPS 1.6/macOS 10.8@
   2241  */
   2242 
   2243 const ipp_uchar_t *			/* O - Date value or @code NULL@ */
   2244 ippGetDate(ipp_attribute_t *attr,	/* I - IPP attribute */
   2245            int             element)	/* I - Value number (0-based) */
   2246 {
   2247  /*
   2248   * Range check input...
   2249   */
   2250 
   2251   if (!attr || attr->value_tag != IPP_TAG_DATE ||
   2252       element < 0 || element >= attr->num_values)
   2253     return (NULL);
   2254 
   2255  /*
   2256   * Return the value...
   2257   */
   2258 
   2259   return (attr->values[element].date);
   2260 }
   2261 
   2262 
   2263 /*
   2264  * 'ippGetGroupTag()' - Get the group associated with an attribute.
   2265  *
   2266  * @since CUPS 1.6/macOS 10.8@
   2267  */
   2268 
   2269 ipp_tag_t				/* O - Group tag or @code IPP_TAG_ZERO@ on error */
   2270 ippGetGroupTag(ipp_attribute_t *attr)	/* I - IPP attribute */
   2271 {
   2272  /*
   2273   * Range check input...
   2274   */
   2275 
   2276   if (!attr)
   2277     return (IPP_TAG_ZERO);
   2278 
   2279  /*
   2280   * Return the group...
   2281   */
   2282 
   2283   return (attr->group_tag);
   2284 }
   2285 
   2286 
   2287 /*
   2288  * 'ippGetInteger()' - Get the integer/enum value for an attribute.
   2289  *
   2290  * The @code element@ parameter specifies which value to get from 0 to
   2291  * @link ippGetCount(attr)@ - 1.
   2292  *
   2293  * @since CUPS 1.6/macOS 10.8@
   2294  */
   2295 
   2296 int					/* O - Value or 0 on error */
   2297 ippGetInteger(ipp_attribute_t *attr,	/* I - IPP attribute */
   2298               int             element)	/* I - Value number (0-based) */
   2299 {
   2300  /*
   2301   * Range check input...
   2302   */
   2303 
   2304   if (!attr || (attr->value_tag != IPP_TAG_INTEGER && attr->value_tag != IPP_TAG_ENUM) ||
   2305       element < 0 || element >= attr->num_values)
   2306     return (0);
   2307 
   2308  /*
   2309   * Return the value...
   2310   */
   2311 
   2312   return (attr->values[element].integer);
   2313 }
   2314 
   2315 
   2316 /*
   2317  * 'ippGetName()' - Get the attribute name.
   2318  *
   2319  * @since CUPS 1.6/macOS 10.8@
   2320  */
   2321 
   2322 const char *				/* O - Attribute name or @code NULL@ for separators */
   2323 ippGetName(ipp_attribute_t *attr)	/* I - IPP attribute */
   2324 {
   2325  /*
   2326   * Range check input...
   2327   */
   2328 
   2329   if (!attr)
   2330     return (NULL);
   2331 
   2332  /*
   2333   * Return the name...
   2334   */
   2335 
   2336   return (attr->name);
   2337 }
   2338 
   2339 
   2340 /*
   2341  * 'ippGetOctetString()' - Get an octetString value from an IPP attribute.
   2342  *
   2343  * The @code element@ parameter specifies which value to get from 0 to
   2344  * @link ippGetCount(attr)@ - 1.
   2345  *
   2346  * @since CUPS 1.7/macOS 10.9@
   2347  */
   2348 
   2349 void *					/* O - Pointer to octetString data */
   2350 ippGetOctetString(
   2351     ipp_attribute_t *attr,		/* I - IPP attribute */
   2352     int             element,		/* I - Value number (0-based) */
   2353     int             *datalen)		/* O - Length of octetString data */
   2354 {
   2355  /*
   2356   * Range check input...
   2357   */
   2358 
   2359   if (!attr || attr->value_tag != IPP_TAG_STRING ||
   2360       element < 0 || element >= attr->num_values)
   2361   {
   2362     if (datalen)
   2363       *datalen = 0;
   2364 
   2365     return (NULL);
   2366   }
   2367 
   2368  /*
   2369   * Return the values...
   2370   */
   2371 
   2372   if (datalen)
   2373     *datalen = attr->values[element].unknown.length;
   2374 
   2375   return (attr->values[element].unknown.data);
   2376 }
   2377 
   2378 
   2379 /*
   2380  * 'ippGetOperation()' - Get the operation ID in an IPP message.
   2381  *
   2382  * @since CUPS 1.6/macOS 10.8@
   2383  */
   2384 
   2385 ipp_op_t				/* O - Operation ID or 0 on error */
   2386 ippGetOperation(ipp_t *ipp)		/* I - IPP request message */
   2387 {
   2388  /*
   2389   * Range check input...
   2390   */
   2391 
   2392   if (!ipp)
   2393     return ((ipp_op_t)0);
   2394 
   2395  /*
   2396   * Return the value...
   2397   */
   2398 
   2399   return (ipp->request.op.operation_id);
   2400 }
   2401 
   2402 
   2403 /*
   2404  * 'ippGetRange()' - Get a rangeOfInteger value from an attribute.
   2405  *
   2406  * The @code element@ parameter specifies which value to get from 0 to
   2407  * @link ippGetCount(attr)@ - 1.
   2408  *
   2409  * @since CUPS 1.6/macOS 10.8@
   2410  */
   2411 
   2412 int					/* O - Lower value of range or 0 */
   2413 ippGetRange(ipp_attribute_t *attr,	/* I - IPP attribute */
   2414 	    int             element,	/* I - Value number (0-based) */
   2415 	    int             *uppervalue)/* O - Upper value of range */
   2416 {
   2417  /*
   2418   * Range check input...
   2419   */
   2420 
   2421   if (!attr || attr->value_tag != IPP_TAG_RANGE ||
   2422       element < 0 || element >= attr->num_values)
   2423   {
   2424     if (uppervalue)
   2425       *uppervalue = 0;
   2426 
   2427     return (0);
   2428   }
   2429 
   2430  /*
   2431   * Return the values...
   2432   */
   2433 
   2434   if (uppervalue)
   2435     *uppervalue = attr->values[element].range.upper;
   2436 
   2437   return (attr->values[element].range.lower);
   2438 }
   2439 
   2440 
   2441 /*
   2442  * 'ippGetRequestId()' - Get the request ID from an IPP message.
   2443  *
   2444  * @since CUPS 1.6/macOS 10.8@
   2445  */
   2446 
   2447 int					/* O - Request ID or 0 on error */
   2448 ippGetRequestId(ipp_t *ipp)		/* I - IPP message */
   2449 {
   2450  /*
   2451   * Range check input...
   2452   */
   2453 
   2454   if (!ipp)
   2455     return (0);
   2456 
   2457  /*
   2458   * Return the request ID...
   2459   */
   2460 
   2461   return (ipp->request.any.request_id);
   2462 }
   2463 
   2464 
   2465 /*
   2466  * 'ippGetResolution()' - Get a resolution value for an attribute.
   2467  *
   2468  * The @code element@ parameter specifies which value to get from 0 to
   2469  * @link ippGetCount(attr)@ - 1.
   2470  *
   2471  * @since CUPS 1.6/macOS 10.8@
   2472  */
   2473 
   2474 int					/* O - Horizontal/cross feed resolution or 0 */
   2475 ippGetResolution(
   2476     ipp_attribute_t *attr,		/* I - IPP attribute */
   2477     int             element,		/* I - Value number (0-based) */
   2478     int             *yres,		/* O - Vertical/feed resolution */
   2479     ipp_res_t       *units)		/* O - Units for resolution */
   2480 {
   2481  /*
   2482   * Range check input...
   2483   */
   2484 
   2485   if (!attr || attr->value_tag != IPP_TAG_RESOLUTION ||
   2486       element < 0 || element >= attr->num_values)
   2487   {
   2488     if (yres)
   2489       *yres = 0;
   2490 
   2491     if (units)
   2492       *units = (ipp_res_t)0;
   2493 
   2494     return (0);
   2495   }
   2496 
   2497  /*
   2498   * Return the value...
   2499   */
   2500 
   2501   if (yres)
   2502     *yres = attr->values[element].resolution.yres;
   2503 
   2504   if (units)
   2505     *units = attr->values[element].resolution.units;
   2506 
   2507   return (attr->values[element].resolution.xres);
   2508 }
   2509 
   2510 
   2511 /*
   2512  * 'ippGetState()' - Get the IPP message state.
   2513  *
   2514  * @since CUPS 1.6/macOS 10.8@
   2515  */
   2516 
   2517 ipp_state_t				/* O - IPP message state value */
   2518 ippGetState(ipp_t *ipp)			/* I - IPP message */
   2519 {
   2520  /*
   2521   * Range check input...
   2522   */
   2523 
   2524   if (!ipp)
   2525     return (IPP_STATE_IDLE);
   2526 
   2527  /*
   2528   * Return the value...
   2529   */
   2530 
   2531   return (ipp->state);
   2532 }
   2533 
   2534 
   2535 /*
   2536  * 'ippGetStatusCode()' - Get the status code from an IPP response or event message.
   2537  *
   2538  * @since CUPS 1.6/macOS 10.8@
   2539  */
   2540 
   2541 ipp_status_t				/* O - Status code in IPP message */
   2542 ippGetStatusCode(ipp_t *ipp)		/* I - IPP response or event message */
   2543 {
   2544  /*
   2545   * Range check input...
   2546   */
   2547 
   2548   if (!ipp)
   2549     return (IPP_STATUS_ERROR_INTERNAL);
   2550 
   2551  /*
   2552   * Return the value...
   2553   */
   2554 
   2555   return (ipp->request.status.status_code);
   2556 }
   2557 
   2558 
   2559 /*
   2560  * 'ippGetString()' - Get the string and optionally the language code for an attribute.
   2561  *
   2562  * The @code element@ parameter specifies which value to get from 0 to
   2563  * @link ippGetCount(attr)@ - 1.
   2564  *
   2565  * @since CUPS 1.6/macOS 10.8@
   2566  */
   2567 
   2568 const char *
   2569 ippGetString(ipp_attribute_t *attr,	/* I - IPP attribute */
   2570              int             element,	/* I - Value number (0-based) */
   2571 	     const char      **language)/* O - Language code (@code NULL@ for don't care) */
   2572 {
   2573   ipp_tag_t	tag;			/* Value tag */
   2574 
   2575 
   2576  /*
   2577   * Range check input...
   2578   */
   2579 
   2580   tag = ippGetValueTag(attr);
   2581 
   2582   if (!attr || element < 0 || element >= attr->num_values || (tag != IPP_TAG_TEXTLANG && tag != IPP_TAG_NAMELANG && (tag < IPP_TAG_TEXT || tag > IPP_TAG_MIMETYPE)))
   2583     return (NULL);
   2584 
   2585  /*
   2586   * Return the value...
   2587   */
   2588 
   2589   if (language)
   2590     *language = attr->values[element].string.language;
   2591 
   2592   return (attr->values[element].string.text);
   2593 }
   2594 
   2595 
   2596 /*
   2597  * 'ippGetValueTag()' - Get the value tag for an attribute.
   2598  *
   2599  * @since CUPS 1.6/macOS 10.8@
   2600  */
   2601 
   2602 ipp_tag_t				/* O - Value tag or @code IPP_TAG_ZERO@ on error */
   2603 ippGetValueTag(ipp_attribute_t *attr)	/* I - IPP attribute */
   2604 {
   2605  /*
   2606   * Range check input...
   2607   */
   2608 
   2609   if (!attr)
   2610     return (IPP_TAG_ZERO);
   2611 
   2612  /*
   2613   * Return the value...
   2614   */
   2615 
   2616   return (attr->value_tag & IPP_TAG_CUPS_MASK);
   2617 }
   2618 
   2619 
   2620 /*
   2621  * 'ippGetVersion()' - Get the major and minor version number from an IPP message.
   2622  *
   2623  * @since CUPS 1.6/macOS 10.8@
   2624  */
   2625 
   2626 int					/* O - Major version number or 0 on error */
   2627 ippGetVersion(ipp_t *ipp,		/* I - IPP message */
   2628               int   *minor)		/* O - Minor version number or @code NULL@ */
   2629 {
   2630  /*
   2631   * Range check input...
   2632   */
   2633 
   2634   if (!ipp)
   2635   {
   2636     if (minor)
   2637       *minor = 0;
   2638 
   2639     return (0);
   2640   }
   2641 
   2642  /*
   2643   * Return the value...
   2644   */
   2645 
   2646   if (minor)
   2647     *minor = ipp->request.any.version[1];
   2648 
   2649   return (ipp->request.any.version[0]);
   2650 }
   2651 
   2652 
   2653 /*
   2654  * 'ippLength()' - Compute the length of an IPP message.
   2655  */
   2656 
   2657 size_t					/* O - Size of IPP message */
   2658 ippLength(ipp_t *ipp)			/* I - IPP message */
   2659 {
   2660   return (ipp_length(ipp, 0));
   2661 }
   2662 
   2663 
   2664 /*
   2665  * 'ippNextAttribute()' - Return the next attribute in the message.
   2666  *
   2667  * @since CUPS 1.6/macOS 10.8@
   2668  */
   2669 
   2670 ipp_attribute_t *			/* O - Next attribute or @code NULL@ if none */
   2671 ippNextAttribute(ipp_t *ipp)		/* I - IPP message */
   2672 {
   2673  /*
   2674   * Range check input...
   2675   */
   2676 
   2677   if (!ipp || !ipp->current)
   2678     return (NULL);
   2679 
   2680  /*
   2681   * Return the next attribute...
   2682   */
   2683 
   2684   return (ipp->current = ipp->current->next);
   2685 }
   2686 
   2687 
   2688 /*
   2689  * 'ippNew()' - Allocate a new IPP message.
   2690  */
   2691 
   2692 ipp_t *					/* O - New IPP message */
   2693 ippNew(void)
   2694 {
   2695   ipp_t			*temp;		/* New IPP message */
   2696   _cups_globals_t	*cg = _cupsGlobals();
   2697 					/* Global data */
   2698 
   2699 
   2700   DEBUG_puts("ippNew()");
   2701 
   2702   if ((temp = (ipp_t *)calloc(1, sizeof(ipp_t))) != NULL)
   2703   {
   2704    /*
   2705     * Set default version - usually 2.0...
   2706     */
   2707 
   2708     if (cg->server_version == 0)
   2709       _cupsSetDefaults();
   2710 
   2711     temp->request.any.version[0] = (ipp_uchar_t)(cg->server_version / 10);
   2712     temp->request.any.version[1] = (ipp_uchar_t)(cg->server_version % 10);
   2713     temp->use                    = 1;
   2714   }
   2715 
   2716   DEBUG_printf(("1ippNew: Returning %p", (void *)temp));
   2717 
   2718   return (temp);
   2719 }
   2720 
   2721 
   2722 /*
   2723  *  'ippNewRequest()' - Allocate a new IPP request message.
   2724  *
   2725  * The new request message is initialized with the attributes-charset and
   2726  * attributes-natural-language attributes added. The
   2727  * attributes-natural-language value is derived from the current locale.
   2728  *
   2729  * @since CUPS 1.2/macOS 10.5@
   2730  */
   2731 
   2732 ipp_t *					/* O - IPP request message */
   2733 ippNewRequest(ipp_op_t op)		/* I - Operation code */
   2734 {
   2735   ipp_t		*request;		/* IPP request message */
   2736   cups_lang_t	*language;		/* Current language localization */
   2737   static int	request_id = 0;		/* Current request ID */
   2738   static _cups_mutex_t request_mutex = _CUPS_MUTEX_INITIALIZER;
   2739 					/* Mutex for request ID */
   2740 
   2741 
   2742   DEBUG_printf(("ippNewRequest(op=%02x(%s))", op, ippOpString(op)));
   2743 
   2744  /*
   2745   * Create a new IPP message...
   2746   */
   2747 
   2748   if ((request = ippNew()) == NULL)
   2749     return (NULL);
   2750 
   2751  /*
   2752   * Set the operation and request ID...
   2753   */
   2754 
   2755   _cupsMutexLock(&request_mutex);
   2756 
   2757   request->request.op.operation_id = op;
   2758   request->request.op.request_id   = ++request_id;
   2759 
   2760   _cupsMutexUnlock(&request_mutex);
   2761 
   2762  /*
   2763   * Use UTF-8 as the character set...
   2764   */
   2765 
   2766   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
   2767                "attributes-charset", NULL, "utf-8");
   2768 
   2769  /*
   2770   * Get the language from the current locale...
   2771   */
   2772 
   2773   language = cupsLangDefault();
   2774 
   2775   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
   2776                "attributes-natural-language", NULL, language->language);
   2777 
   2778  /*
   2779   * Return the new request...
   2780   */
   2781 
   2782   return (request);
   2783 }
   2784 
   2785 
   2786 /*
   2787  * 'ippNewResponse()' - Allocate a new IPP response message.
   2788  *
   2789  * The new response message is initialized with the same version-number,
   2790  * request-id, attributes-charset, and attributes-natural-language as the
   2791  * provided request message.  If the attributes-charset or
   2792  * attributes-natural-language attributes are missing from the request,
   2793  * "utf-8" and a value derived from the current locale are substituted,
   2794  * respectively.
   2795  *
   2796  * @since CUPS 1.7/macOS 10.9@
   2797  */
   2798 
   2799 ipp_t *					/* O - IPP response message */
   2800 ippNewResponse(ipp_t *request)		/* I - IPP request message */
   2801 {
   2802   ipp_t			*response;	/* IPP response message */
   2803   ipp_attribute_t	*attr;		/* Current attribute */
   2804 
   2805 
   2806  /*
   2807   * Range check input...
   2808   */
   2809 
   2810   if (!request)
   2811     return (NULL);
   2812 
   2813  /*
   2814   * Create a new IPP message...
   2815   */
   2816 
   2817   if ((response = ippNew()) == NULL)
   2818     return (NULL);
   2819 
   2820  /*
   2821   * Copy the request values over to the response...
   2822   */
   2823 
   2824   response->request.status.version[0] = request->request.op.version[0];
   2825   response->request.status.version[1] = request->request.op.version[1];
   2826   response->request.status.request_id = request->request.op.request_id;
   2827 
   2828  /*
   2829   * The first attribute MUST be attributes-charset...
   2830   */
   2831 
   2832   attr = request->attrs;
   2833 
   2834   if (attr && attr->name && !strcmp(attr->name, "attributes-charset") &&
   2835       attr->group_tag == IPP_TAG_OPERATION &&
   2836       attr->value_tag == IPP_TAG_CHARSET &&
   2837       attr->num_values == 1)
   2838   {
   2839    /*
   2840     * Copy charset from request...
   2841     */
   2842 
   2843     ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
   2844 		 "attributes-charset", NULL, attr->values[0].string.text);
   2845   }
   2846   else
   2847   {
   2848    /*
   2849     * Use "utf-8" as the default...
   2850     */
   2851 
   2852     ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
   2853 		 "attributes-charset", NULL, "utf-8");
   2854   }
   2855 
   2856  /*
   2857   * Then attributes-natural-language...
   2858   */
   2859 
   2860   if (attr)
   2861     attr = attr->next;
   2862 
   2863   if (attr && attr->name &&
   2864       !strcmp(attr->name, "attributes-natural-language") &&
   2865       attr->group_tag == IPP_TAG_OPERATION &&
   2866       attr->value_tag == IPP_TAG_LANGUAGE &&
   2867       attr->num_values == 1)
   2868   {
   2869    /*
   2870     * Copy language from request...
   2871     */
   2872 
   2873     ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
   2874 		 "attributes-natural-language", NULL,
   2875 		 attr->values[0].string.text);
   2876   }
   2877   else
   2878   {
   2879    /*
   2880     * Use the language from the current locale...
   2881     */
   2882 
   2883     cups_lang_t *language = cupsLangDefault();
   2884 					/* Current locale */
   2885 
   2886     ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
   2887 		 "attributes-natural-language", NULL, language->language);
   2888   }
   2889 
   2890   return (response);
   2891 }
   2892 
   2893 
   2894 /*
   2895  * 'ippRead()' - Read data for an IPP message from a HTTP connection.
   2896  */
   2897 
   2898 ipp_state_t				/* O - Current state */
   2899 ippRead(http_t *http,			/* I - HTTP connection */
   2900         ipp_t  *ipp)			/* I - IPP data */
   2901 {
   2902   DEBUG_printf(("ippRead(http=%p, ipp=%p), data_remaining=" CUPS_LLFMT, (void *)http, (void *)ipp, CUPS_LLCAST (http ? http->data_remaining : -1)));
   2903 
   2904   if (!http)
   2905     return (IPP_STATE_ERROR);
   2906 
   2907   DEBUG_printf(("2ippRead: http->state=%d, http->used=%d", http->state, http->used));
   2908 
   2909   return (ippReadIO(http, (ipp_iocb_t)ipp_read_http, http->blocking, NULL,
   2910                     ipp));
   2911 }
   2912 
   2913 
   2914 /*
   2915  * 'ippReadFile()' - Read data for an IPP message from a file.
   2916  *
   2917  * @since CUPS 1.1.19/macOS 10.3@
   2918  */
   2919 
   2920 ipp_state_t				/* O - Current state */
   2921 ippReadFile(int   fd,			/* I - HTTP data */
   2922             ipp_t *ipp)			/* I - IPP data */
   2923 {
   2924   DEBUG_printf(("ippReadFile(fd=%d, ipp=%p)", fd, (void *)ipp));
   2925 
   2926   return (ippReadIO(&fd, (ipp_iocb_t)ipp_read_file, 1, NULL, ipp));
   2927 }
   2928 
   2929 
   2930 /*
   2931  * 'ippReadIO()' - Read data for an IPP message.
   2932  *
   2933  * @since CUPS 1.2/macOS 10.5@
   2934  */
   2935 
   2936 ipp_state_t				/* O - Current state */
   2937 ippReadIO(void       *src,		/* I - Data source */
   2938           ipp_iocb_t cb,		/* I - Read callback function */
   2939 	  int        blocking,		/* I - Use blocking IO? */
   2940 	  ipp_t      *parent,		/* I - Parent request, if any */
   2941           ipp_t      *ipp)		/* I - IPP data */
   2942 {
   2943   int			n;		/* Length of data */
   2944   unsigned char		*buffer,	/* Data buffer */
   2945 			string[IPP_MAX_TEXT],
   2946 					/* Small string buffer */
   2947 			*bufptr;	/* Pointer into buffer */
   2948   ipp_attribute_t	*attr;		/* Current attribute */
   2949   ipp_tag_t		tag;		/* Current tag */
   2950   ipp_tag_t		value_tag;	/* Current value tag */
   2951   _ipp_value_t		*value;		/* Current value */
   2952 
   2953 
   2954   DEBUG_printf(("ippReadIO(src=%p, cb=%p, blocking=%d, parent=%p, ipp=%p)", (void *)src, (void *)cb, blocking, (void *)parent, (void *)ipp));
   2955   DEBUG_printf(("2ippReadIO: ipp->state=%d", ipp ? ipp->state : IPP_STATE_ERROR));
   2956 
   2957   if (!src || !ipp)
   2958     return (IPP_STATE_ERROR);
   2959 
   2960   if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL)
   2961   {
   2962     DEBUG_puts("1ippReadIO: Unable to get read buffer.");
   2963     return (IPP_STATE_ERROR);
   2964   }
   2965 
   2966   switch (ipp->state)
   2967   {
   2968     case IPP_STATE_IDLE :
   2969         ipp->state ++; /* Avoid common problem... */
   2970 
   2971     case IPP_STATE_HEADER :
   2972         if (parent == NULL)
   2973 	{
   2974 	 /*
   2975           * Get the request header...
   2976 	  */
   2977 
   2978           if ((*cb)(src, buffer, 8) < 8)
   2979 	  {
   2980 	    DEBUG_puts("1ippReadIO: Unable to read header.");
   2981 	    _cupsBufferRelease((char *)buffer);
   2982 	    return (IPP_STATE_ERROR);
   2983 	  }
   2984 
   2985 	 /*
   2986           * Then copy the request header over...
   2987 	  */
   2988 
   2989           ipp->request.any.version[0]  = buffer[0];
   2990           ipp->request.any.version[1]  = buffer[1];
   2991           ipp->request.any.op_status   = (buffer[2] << 8) | buffer[3];
   2992           ipp->request.any.request_id  = (((((buffer[4] << 8) | buffer[5]) << 8) |
   2993 	                        	 buffer[6]) << 8) | buffer[7];
   2994 
   2995           DEBUG_printf(("2ippReadIO: version=%d.%d", buffer[0], buffer[1]));
   2996 	  DEBUG_printf(("2ippReadIO: op_status=%04x",
   2997 	                ipp->request.any.op_status));
   2998 	  DEBUG_printf(("2ippReadIO: request_id=%d",
   2999 	                ipp->request.any.request_id));
   3000         }
   3001 
   3002         ipp->state   = IPP_STATE_ATTRIBUTE;
   3003 	ipp->current = NULL;
   3004 	ipp->curtag  = IPP_TAG_ZERO;
   3005 	ipp->prev    = ipp->last;
   3006 
   3007        /*
   3008         * If blocking is disabled, stop here...
   3009 	*/
   3010 
   3011         if (!blocking)
   3012 	  break;
   3013 
   3014     case IPP_STATE_ATTRIBUTE :
   3015         for (;;)
   3016 	{
   3017 	  if ((*cb)(src, buffer, 1) < 1)
   3018 	  {
   3019 	    DEBUG_puts("1ippReadIO: Callback returned EOF/error");
   3020 	    _cupsBufferRelease((char *)buffer);
   3021 	    return (IPP_STATE_ERROR);
   3022 	  }
   3023 
   3024 	  DEBUG_printf(("2ippReadIO: ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev));
   3025 
   3026 	 /*
   3027 	  * Read this attribute...
   3028 	  */
   3029 
   3030           tag = (ipp_tag_t)buffer[0];
   3031           if (tag == IPP_TAG_EXTENSION)
   3032           {
   3033            /*
   3034             * Read 32-bit "extension" tag...
   3035             */
   3036 
   3037 	    if ((*cb)(src, buffer, 4) < 1)
   3038 	    {
   3039 	      DEBUG_puts("1ippReadIO: Callback returned EOF/error");
   3040 	      _cupsBufferRelease((char *)buffer);
   3041 	      return (IPP_STATE_ERROR);
   3042 	    }
   3043 
   3044 	    tag = (ipp_tag_t)((((((buffer[0] << 8) | buffer[1]) << 8) |
   3045 	                        buffer[2]) << 8) | buffer[3]);
   3046 
   3047             if (tag & IPP_TAG_CUPS_CONST)
   3048             {
   3049              /*
   3050               * Fail if the high bit is set in the tag...
   3051               */
   3052 
   3053 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP extension tag larger than 0x7FFFFFFF."), 1);
   3054 	      DEBUG_printf(("1ippReadIO: bad tag 0x%x.", tag));
   3055 	      _cupsBufferRelease((char *)buffer);
   3056 	      return (IPP_STATE_ERROR);
   3057             }
   3058           }
   3059 
   3060 	  if (tag == IPP_TAG_END)
   3061 	  {
   3062 	   /*
   3063 	    * No more attributes left...
   3064 	    */
   3065 
   3066             DEBUG_puts("2ippReadIO: IPP_TAG_END.");
   3067 
   3068 	    ipp->state = IPP_STATE_DATA;
   3069 	    break;
   3070 	  }
   3071           else if (tag < IPP_TAG_UNSUPPORTED_VALUE)
   3072 	  {
   3073 	   /*
   3074 	    * Group tag...  Set the current group and continue...
   3075 	    */
   3076 
   3077             if (ipp->curtag == tag)
   3078 	      ipp->prev = ippAddSeparator(ipp);
   3079             else if (ipp->current)
   3080 	      ipp->prev = ipp->current;
   3081 
   3082 	    ipp->curtag  = tag;
   3083 	    ipp->current = NULL;
   3084 	    DEBUG_printf(("2ippReadIO: group tag=%x(%s), ipp->prev=%p", tag, ippTagString(tag), (void *)ipp->prev));
   3085 	    continue;
   3086 	  }
   3087 
   3088           DEBUG_printf(("2ippReadIO: value tag=%x(%s)", tag,
   3089 	                ippTagString(tag)));
   3090 
   3091          /*
   3092 	  * Get the name...
   3093 	  */
   3094 
   3095           if ((*cb)(src, buffer, 2) < 2)
   3096 	  {
   3097 	    DEBUG_puts("1ippReadIO: unable to read name length.");
   3098 	    _cupsBufferRelease((char *)buffer);
   3099 	    return (IPP_STATE_ERROR);
   3100 	  }
   3101 
   3102           n = (buffer[0] << 8) | buffer[1];
   3103 
   3104           if (n >= IPP_BUF_SIZE)
   3105 	  {
   3106 	    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP name larger than 32767 bytes."), 1);
   3107 	    DEBUG_printf(("1ippReadIO: bad name length %d.", n));
   3108 	    _cupsBufferRelease((char *)buffer);
   3109 	    return (IPP_STATE_ERROR);
   3110 	  }
   3111 
   3112           DEBUG_printf(("2ippReadIO: name length=%d", n));
   3113 
   3114           if (n == 0 && tag != IPP_TAG_MEMBERNAME &&
   3115 	      tag != IPP_TAG_END_COLLECTION)
   3116 	  {
   3117 	   /*
   3118 	    * More values for current attribute...
   3119 	    */
   3120 
   3121             if (ipp->current == NULL)
   3122 	    {
   3123 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP attribute has no name."), 1);
   3124 	      DEBUG_puts("1ippReadIO: Attribute without name and no current.");
   3125 	      _cupsBufferRelease((char *)buffer);
   3126 	      return (IPP_STATE_ERROR);
   3127 	    }
   3128 
   3129             attr      = ipp->current;
   3130 	    value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_CUPS_MASK);
   3131 
   3132 	   /*
   3133 	    * Make sure we aren't adding a new value of a different
   3134 	    * type...
   3135 	    */
   3136 
   3137 	    if (value_tag == IPP_TAG_ZERO)
   3138 	    {
   3139 	     /*
   3140 	      * Setting the value of a collection member...
   3141 	      */
   3142 
   3143 	      attr->value_tag = tag;
   3144 	    }
   3145 	    else if (value_tag == IPP_TAG_TEXTLANG ||
   3146 	             value_tag == IPP_TAG_NAMELANG ||
   3147 		     (value_tag >= IPP_TAG_TEXT &&
   3148 		      value_tag <= IPP_TAG_MIMETYPE))
   3149             {
   3150 	     /*
   3151 	      * String values can sometimes come across in different
   3152 	      * forms; accept sets of differing values...
   3153 	      */
   3154 
   3155 	      if (tag != IPP_TAG_TEXTLANG && tag != IPP_TAG_NAMELANG &&
   3156 	          (tag < IPP_TAG_TEXT || tag > IPP_TAG_MIMETYPE) &&
   3157 		  tag != IPP_TAG_NOVALUE)
   3158 	      {
   3159 		_cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3160 		              _("IPP 1setOf attribute with incompatible value "
   3161 		                "tags."), 1);
   3162 		DEBUG_printf(("1ippReadIO: 1setOf value tag %x(%s) != %x(%s)",
   3163 			      value_tag, ippTagString(value_tag), tag,
   3164 			      ippTagString(tag)));
   3165 		_cupsBufferRelease((char *)buffer);
   3166 	        return (IPP_STATE_ERROR);
   3167 	      }
   3168 
   3169               if (value_tag != tag)
   3170               {
   3171                 DEBUG_printf(("1ippReadIO: Converting %s attribute from %s to %s.",
   3172                               attr->name, ippTagString(value_tag), ippTagString(tag)));
   3173 		ippSetValueTag(ipp, &attr, tag);
   3174 	      }
   3175             }
   3176 	    else if (value_tag == IPP_TAG_INTEGER ||
   3177 	             value_tag == IPP_TAG_RANGE)
   3178             {
   3179 	     /*
   3180 	      * Integer and rangeOfInteger values can sometimes be mixed; accept
   3181 	      * sets of differing values...
   3182 	      */
   3183 
   3184 	      if (tag != IPP_TAG_INTEGER && tag != IPP_TAG_RANGE)
   3185 	      {
   3186 		_cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3187 		              _("IPP 1setOf attribute with incompatible value "
   3188 		                "tags."), 1);
   3189 		DEBUG_printf(("1ippReadIO: 1setOf value tag %x(%s) != %x(%s)",
   3190 			      value_tag, ippTagString(value_tag), tag,
   3191 			      ippTagString(tag)));
   3192 		_cupsBufferRelease((char *)buffer);
   3193 	        return (IPP_STATE_ERROR);
   3194 	      }
   3195 
   3196               if (value_tag == IPP_TAG_INTEGER && tag == IPP_TAG_RANGE)
   3197               {
   3198                /*
   3199                 * Convert integer values to rangeOfInteger values...
   3200                 */
   3201 
   3202 		DEBUG_printf(("1ippReadIO: Converting %s attribute to "
   3203 		              "rangeOfInteger.", attr->name));
   3204                 ippSetValueTag(ipp, &attr, IPP_TAG_RANGE);
   3205               }
   3206             }
   3207 	    else if (value_tag != tag)
   3208 	    {
   3209 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3210 			    _("IPP 1setOf attribute with incompatible value "
   3211 			      "tags."), 1);
   3212 	      DEBUG_printf(("1ippReadIO: value tag %x(%s) != %x(%s)",
   3213 	                    value_tag, ippTagString(value_tag), tag,
   3214 			    ippTagString(tag)));
   3215 	      _cupsBufferRelease((char *)buffer);
   3216 	      return (IPP_STATE_ERROR);
   3217             }
   3218 
   3219            /*
   3220 	    * Finally, reallocate the attribute array as needed...
   3221 	    */
   3222 
   3223 	    if ((value = ipp_set_value(ipp, &attr, attr->num_values)) == NULL)
   3224 	    {
   3225 	      _cupsBufferRelease((char *)buffer);
   3226 	      return (IPP_STATE_ERROR);
   3227 	    }
   3228 	  }
   3229 	  else if (tag == IPP_TAG_MEMBERNAME)
   3230 	  {
   3231 	   /*
   3232 	    * Name must be length 0!
   3233 	    */
   3234 
   3235 	    if (n)
   3236 	    {
   3237 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP member name is not empty."), 1);
   3238 	      DEBUG_puts("1ippReadIO: member name not empty.");
   3239 	      _cupsBufferRelease((char *)buffer);
   3240 	      return (IPP_STATE_ERROR);
   3241 	    }
   3242 
   3243             if (ipp->current)
   3244 	      ipp->prev = ipp->current;
   3245 
   3246 	    attr = ipp->current = ipp_add_attr(ipp, NULL, ipp->curtag, IPP_TAG_ZERO, 1);
   3247 	    if (!attr)
   3248 	    {
   3249 	      _cupsSetHTTPError(HTTP_STATUS_ERROR);
   3250 	      DEBUG_puts("1ippReadIO: unable to allocate attribute.");
   3251 	      _cupsBufferRelease((char *)buffer);
   3252 	      return (IPP_STATE_ERROR);
   3253 	    }
   3254 
   3255 	    DEBUG_printf(("2ippReadIO: membername, ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev));
   3256 
   3257 	    value = attr->values;
   3258 	  }
   3259 	  else if (tag != IPP_TAG_END_COLLECTION)
   3260 	  {
   3261 	   /*
   3262 	    * New attribute; read the name and add it...
   3263 	    */
   3264 
   3265 	    if ((*cb)(src, buffer, (size_t)n) < n)
   3266 	    {
   3267 	      DEBUG_puts("1ippReadIO: unable to read name.");
   3268 	      _cupsBufferRelease((char *)buffer);
   3269 	      return (IPP_STATE_ERROR);
   3270 	    }
   3271 
   3272 	    buffer[n] = '\0';
   3273 
   3274             if (ipp->current)
   3275 	      ipp->prev = ipp->current;
   3276 
   3277 	    if ((attr = ipp->current = ipp_add_attr(ipp, (char *)buffer, ipp->curtag, tag,
   3278 	                                            1)) == NULL)
   3279 	    {
   3280 	      _cupsSetHTTPError(HTTP_STATUS_ERROR);
   3281 	      DEBUG_puts("1ippReadIO: unable to allocate attribute.");
   3282 	      _cupsBufferRelease((char *)buffer);
   3283 	      return (IPP_STATE_ERROR);
   3284 	    }
   3285 
   3286 	    DEBUG_printf(("2ippReadIO: name=\"%s\", ipp->current=%p, ipp->prev=%p", buffer, (void *)ipp->current, (void *)ipp->prev));
   3287 
   3288 	    value = attr->values;
   3289 	  }
   3290 	  else
   3291 	  {
   3292 	    attr  = NULL;
   3293 	    value = NULL;
   3294 	  }
   3295 
   3296 	  if ((*cb)(src, buffer, 2) < 2)
   3297 	  {
   3298 	    DEBUG_puts("1ippReadIO: unable to read value length.");
   3299 	    _cupsBufferRelease((char *)buffer);
   3300 	    return (IPP_STATE_ERROR);
   3301 	  }
   3302 
   3303 	  n = (buffer[0] << 8) | buffer[1];
   3304           DEBUG_printf(("2ippReadIO: value length=%d", n));
   3305 
   3306 	  if (n >= IPP_BUF_SIZE)
   3307 	  {
   3308 	    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3309 			  _("IPP value larger than 32767 bytes."), 1);
   3310 	    DEBUG_printf(("1ippReadIO: bad value length %d.", n));
   3311 	    _cupsBufferRelease((char *)buffer);
   3312 	    return (IPP_STATE_ERROR);
   3313 	  }
   3314 
   3315 	  switch (tag)
   3316 	  {
   3317 	    case IPP_TAG_INTEGER :
   3318 	    case IPP_TAG_ENUM :
   3319 		if (n != 4)
   3320 		{
   3321 		  if (tag == IPP_TAG_INTEGER)
   3322 		    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3323 				  _("IPP integer value not 4 bytes."), 1);
   3324 		  else
   3325 		    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3326 				  _("IPP enum value not 4 bytes."), 1);
   3327 		  DEBUG_printf(("1ippReadIO: bad integer value length %d.", n));
   3328 		  _cupsBufferRelease((char *)buffer);
   3329 		  return (IPP_STATE_ERROR);
   3330 		}
   3331 
   3332 	        if ((*cb)(src, buffer, 4) < 4)
   3333 		{
   3334 	          DEBUG_puts("1ippReadIO: Unable to read integer value.");
   3335 		  _cupsBufferRelease((char *)buffer);
   3336 		  return (IPP_STATE_ERROR);
   3337 		}
   3338 
   3339 		n = (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
   3340 		    buffer[3];
   3341 
   3342                 if (attr->value_tag == IPP_TAG_RANGE)
   3343                   value->range.lower = value->range.upper = n;
   3344                 else
   3345 		  value->integer = n;
   3346 	        break;
   3347 
   3348 	    case IPP_TAG_BOOLEAN :
   3349 		if (n != 1)
   3350 		{
   3351 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP boolean value not 1 byte."),
   3352 		                1);
   3353 		  DEBUG_printf(("1ippReadIO: bad boolean value length %d.", n));
   3354 		  _cupsBufferRelease((char *)buffer);
   3355 		  return (IPP_STATE_ERROR);
   3356 		}
   3357 
   3358 	        if ((*cb)(src, buffer, 1) < 1)
   3359 		{
   3360 	          DEBUG_puts("1ippReadIO: Unable to read boolean value.");
   3361 		  _cupsBufferRelease((char *)buffer);
   3362 		  return (IPP_STATE_ERROR);
   3363 		}
   3364 
   3365                 value->boolean = (char)buffer[0];
   3366 	        break;
   3367 
   3368             case IPP_TAG_NOVALUE :
   3369 	    case IPP_TAG_NOTSETTABLE :
   3370 	    case IPP_TAG_DELETEATTR :
   3371 	    case IPP_TAG_ADMINDEFINE :
   3372 	       /*
   3373 	        * These value types are not supposed to have values, however
   3374 		* some vendors (Brother) do not implement IPP correctly and so
   3375 		* we need to map non-empty values to text...
   3376 		*/
   3377 
   3378 	        if (attr->value_tag == tag)
   3379 		{
   3380 		  if (n == 0)
   3381 		    break;
   3382 
   3383 		  attr->value_tag = IPP_TAG_TEXT;
   3384 		}
   3385 
   3386 	    case IPP_TAG_TEXT :
   3387 	    case IPP_TAG_NAME :
   3388 	    case IPP_TAG_KEYWORD :
   3389 	    case IPP_TAG_URI :
   3390 	    case IPP_TAG_URISCHEME :
   3391 	    case IPP_TAG_CHARSET :
   3392 	    case IPP_TAG_LANGUAGE :
   3393 	    case IPP_TAG_MIMETYPE :
   3394 	        if (n > 0)
   3395 	        {
   3396 		  if ((*cb)(src, buffer, (size_t)n) < n)
   3397 		  {
   3398 		    DEBUG_puts("1ippReadIO: unable to read string value.");
   3399 		    _cupsBufferRelease((char *)buffer);
   3400 		    return (IPP_STATE_ERROR);
   3401 		  }
   3402 		}
   3403 
   3404 		buffer[n] = '\0';
   3405 		value->string.text = _cupsStrAlloc((char *)buffer);
   3406 		DEBUG_printf(("2ippReadIO: value=\"%s\"", value->string.text));
   3407 	        break;
   3408 
   3409 	    case IPP_TAG_DATE :
   3410 		if (n != 11)
   3411 		{
   3412 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP date value not 11 bytes."), 1);
   3413 		  DEBUG_printf(("1ippReadIO: bad date value length %d.", n));
   3414 		  _cupsBufferRelease((char *)buffer);
   3415 		  return (IPP_STATE_ERROR);
   3416 		}
   3417 
   3418 	        if ((*cb)(src, value->date, 11) < 11)
   3419 		{
   3420 	          DEBUG_puts("1ippReadIO: Unable to read date value.");
   3421 		  _cupsBufferRelease((char *)buffer);
   3422 		  return (IPP_STATE_ERROR);
   3423 		}
   3424 	        break;
   3425 
   3426 	    case IPP_TAG_RESOLUTION :
   3427 		if (n != 9)
   3428 		{
   3429 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3430 		                _("IPP resolution value not 9 bytes."), 1);
   3431 		  DEBUG_printf(("1ippReadIO: bad resolution value length %d.", n));
   3432 		  _cupsBufferRelease((char *)buffer);
   3433 		  return (IPP_STATE_ERROR);
   3434 		}
   3435 
   3436 	        if ((*cb)(src, buffer, 9) < 9)
   3437 		{
   3438 	          DEBUG_puts("1ippReadIO: Unable to read resolution value.");
   3439 		  _cupsBufferRelease((char *)buffer);
   3440 		  return (IPP_STATE_ERROR);
   3441 		}
   3442 
   3443                 value->resolution.xres =
   3444 		    (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
   3445 		    buffer[3];
   3446                 value->resolution.yres =
   3447 		    (((((buffer[4] << 8) | buffer[5]) << 8) | buffer[6]) << 8) |
   3448 		    buffer[7];
   3449                 value->resolution.units =
   3450 		    (ipp_res_t)buffer[8];
   3451 	        break;
   3452 
   3453 	    case IPP_TAG_RANGE :
   3454 		if (n != 8)
   3455 		{
   3456 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3457 		                _("IPP rangeOfInteger value not 8 bytes."), 1);
   3458 		  DEBUG_printf(("1ippReadIO: bad rangeOfInteger value length "
   3459 		                "%d.", n));
   3460 		  _cupsBufferRelease((char *)buffer);
   3461 		  return (IPP_STATE_ERROR);
   3462 		}
   3463 
   3464 	        if ((*cb)(src, buffer, 8) < 8)
   3465 		{
   3466 	          DEBUG_puts("1ippReadIO: Unable to read range value.");
   3467 		  _cupsBufferRelease((char *)buffer);
   3468 		  return (IPP_STATE_ERROR);
   3469 		}
   3470 
   3471                 value->range.lower =
   3472 		    (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
   3473 		    buffer[3];
   3474                 value->range.upper =
   3475 		    (((((buffer[4] << 8) | buffer[5]) << 8) | buffer[6]) << 8) |
   3476 		    buffer[7];
   3477 	        break;
   3478 
   3479 	    case IPP_TAG_TEXTLANG :
   3480 	    case IPP_TAG_NAMELANG :
   3481 	        if (n < 4)
   3482 		{
   3483 		  if (tag == IPP_TAG_TEXTLANG)
   3484 		    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3485 		                  _("IPP textWithLanguage value less than "
   3486 		                    "minimum 4 bytes."), 1);
   3487 		  else
   3488 		    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3489 		                  _("IPP nameWithLanguage value less than "
   3490 		                    "minimum 4 bytes."), 1);
   3491 		  DEBUG_printf(("1ippReadIO: bad stringWithLanguage value "
   3492 		                "length %d.", n));
   3493 		  _cupsBufferRelease((char *)buffer);
   3494 		  return (IPP_STATE_ERROR);
   3495 		}
   3496 
   3497 	        if ((*cb)(src, buffer, (size_t)n) < n)
   3498 		{
   3499 	          DEBUG_puts("1ippReadIO: Unable to read string w/language "
   3500 		             "value.");
   3501 		  _cupsBufferRelease((char *)buffer);
   3502 		  return (IPP_STATE_ERROR);
   3503 		}
   3504 
   3505                 bufptr = buffer;
   3506 
   3507 	       /*
   3508 	        * text-with-language and name-with-language are composite
   3509 		* values:
   3510 		*
   3511 		*    language-length
   3512 		*    language
   3513 		*    text-length
   3514 		*    text
   3515 		*/
   3516 
   3517 		n = (bufptr[0] << 8) | bufptr[1];
   3518 
   3519 		if ((bufptr + 2 + n) >= (buffer + IPP_BUF_SIZE) || n >= (int)sizeof(string))
   3520 		{
   3521 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3522 		                _("IPP language length overflows value."), 1);
   3523 		  DEBUG_printf(("1ippReadIO: bad language value length %d.",
   3524 		                n));
   3525 		  _cupsBufferRelease((char *)buffer);
   3526 		  return (IPP_STATE_ERROR);
   3527 		}
   3528 		else if (n >= IPP_MAX_LANGUAGE)
   3529 		{
   3530 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3531 		                _("IPP language length too large."), 1);
   3532 		  DEBUG_printf(("1ippReadIO: bad language value length %d.",
   3533 		                n));
   3534 		  _cupsBufferRelease((char *)buffer);
   3535 		  return (IPP_STATE_ERROR);
   3536 		}
   3537 
   3538 		memcpy(string, bufptr + 2, (size_t)n);
   3539 		string[n] = '\0';
   3540 
   3541 		value->string.language = _cupsStrAlloc((char *)string);
   3542 
   3543                 bufptr += 2 + n;
   3544 		n = (bufptr[0] << 8) | bufptr[1];
   3545 
   3546 		if ((bufptr + 2 + n) >= (buffer + IPP_BUF_SIZE))
   3547 		{
   3548 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3549 		                _("IPP string length overflows value."), 1);
   3550 		  DEBUG_printf(("1ippReadIO: bad string value length %d.", n));
   3551 		  _cupsBufferRelease((char *)buffer);
   3552 		  return (IPP_STATE_ERROR);
   3553 		}
   3554 
   3555 		bufptr[2 + n] = '\0';
   3556                 value->string.text = _cupsStrAlloc((char *)bufptr + 2);
   3557 	        break;
   3558 
   3559             case IPP_TAG_BEGIN_COLLECTION :
   3560 	       /*
   3561 	        * Oh, boy, here comes a collection value, so read it...
   3562 		*/
   3563 
   3564                 value->collection = ippNew();
   3565 
   3566                 if (n > 0)
   3567 		{
   3568 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3569 		                _("IPP begCollection value not 0 bytes."), 1);
   3570 	          DEBUG_puts("1ippReadIO: begCollection tag with value length "
   3571 		             "> 0.");
   3572 		  _cupsBufferRelease((char *)buffer);
   3573 		  return (IPP_STATE_ERROR);
   3574 		}
   3575 
   3576 		if (ippReadIO(src, cb, 1, ipp, value->collection) == IPP_STATE_ERROR)
   3577 		{
   3578 	          DEBUG_puts("1ippReadIO: Unable to read collection value.");
   3579 		  _cupsBufferRelease((char *)buffer);
   3580 		  return (IPP_STATE_ERROR);
   3581 		}
   3582                 break;
   3583 
   3584             case IPP_TAG_END_COLLECTION :
   3585 		_cupsBufferRelease((char *)buffer);
   3586 
   3587                 if (n > 0)
   3588 		{
   3589 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3590 		                _("IPP endCollection value not 0 bytes."), 1);
   3591 	          DEBUG_puts("1ippReadIO: endCollection tag with value length "
   3592 		             "> 0.");
   3593 		  return (IPP_STATE_ERROR);
   3594 		}
   3595 
   3596 	        DEBUG_puts("1ippReadIO: endCollection tag...");
   3597 		return (ipp->state = IPP_STATE_DATA);
   3598 
   3599             case IPP_TAG_MEMBERNAME :
   3600 	       /*
   3601 	        * The value the name of the member in the collection, which
   3602 		* we need to carry over...
   3603 		*/
   3604 
   3605                 if (!attr)
   3606                 {
   3607 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3608 		                _("IPP memberName with no attribute."), 1);
   3609 	          DEBUG_puts("1ippReadIO: Member name without attribute.");
   3610 		  _cupsBufferRelease((char *)buffer);
   3611 		  return (IPP_STATE_ERROR);
   3612                 }
   3613 		else if (n == 0)
   3614 		{
   3615 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3616 		                _("IPP memberName value is empty."), 1);
   3617 	          DEBUG_puts("1ippReadIO: Empty member name value.");
   3618 		  _cupsBufferRelease((char *)buffer);
   3619 		  return (IPP_STATE_ERROR);
   3620 		}
   3621 		else if ((*cb)(src, buffer, (size_t)n) < n)
   3622 		{
   3623 	          DEBUG_puts("1ippReadIO: Unable to read member name value.");
   3624 		  _cupsBufferRelease((char *)buffer);
   3625 		  return (IPP_STATE_ERROR);
   3626 		}
   3627 
   3628 		buffer[n] = '\0';
   3629 		attr->name = _cupsStrAlloc((char *)buffer);
   3630 
   3631                /*
   3632 	        * Since collection members are encoded differently than
   3633 		* regular attributes, make sure we don't start with an
   3634 		* empty value...
   3635 		*/
   3636 
   3637                 attr->num_values --;
   3638 
   3639 		DEBUG_printf(("2ippReadIO: member name=\"%s\"", attr->name));
   3640 		break;
   3641 
   3642             default : /* Other unsupported values */
   3643                 if (tag == IPP_TAG_STRING && n > IPP_MAX_LENGTH)
   3644 		{
   3645 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   3646 		                _("IPP octetString length too large."), 1);
   3647 		  DEBUG_printf(("1ippReadIO: bad octetString value length %d.",
   3648 		                n));
   3649 		  _cupsBufferRelease((char *)buffer);
   3650 		  return (IPP_STATE_ERROR);
   3651 		}
   3652 
   3653                 value->unknown.length = n;
   3654 
   3655 	        if (n > 0)
   3656 		{
   3657 		  if ((value->unknown.data = malloc((size_t)n)) == NULL)
   3658 		  {
   3659 		    _cupsSetHTTPError(HTTP_STATUS_ERROR);
   3660 		    DEBUG_puts("1ippReadIO: Unable to allocate value");
   3661 		    _cupsBufferRelease((char *)buffer);
   3662 		    return (IPP_STATE_ERROR);
   3663 		  }
   3664 
   3665 	          if ((*cb)(src, value->unknown.data, (size_t)n) < n)
   3666 		  {
   3667 	            DEBUG_puts("1ippReadIO: Unable to read unsupported value.");
   3668 		    _cupsBufferRelease((char *)buffer);
   3669 		    return (IPP_STATE_ERROR);
   3670 		  }
   3671 		}
   3672 		else
   3673 		  value->unknown.data = NULL;
   3674 	        break;
   3675 	  }
   3676 
   3677 	 /*
   3678           * If blocking is disabled, stop here...
   3679 	  */
   3680 
   3681           if (!blocking)
   3682 	    break;
   3683 	}
   3684         break;
   3685 
   3686     case IPP_STATE_DATA :
   3687         break;
   3688 
   3689     default :
   3690         break; /* anti-compiler-warning-code */
   3691   }
   3692 
   3693   DEBUG_printf(("1ippReadIO: returning ipp->state=%d.", ipp->state));
   3694   _cupsBufferRelease((char *)buffer);
   3695 
   3696   return (ipp->state);
   3697 }
   3698 
   3699 
   3700 /*
   3701  * 'ippSetBoolean()' - Set a boolean value in an attribute.
   3702  *
   3703  * The @code ipp@ parameter refers to an IPP message previously created using
   3704  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   3705  *
   3706  * The @code attr@ parameter may be modified as a result of setting the value.
   3707  *
   3708  * The @code element@ parameter specifies which value to set from 0 to
   3709  * @link ippGetCount(attr)@.
   3710  *
   3711  * @since CUPS 1.6/macOS 10.8@
   3712  */
   3713 
   3714 int					/* O  - 1 on success, 0 on failure */
   3715 ippSetBoolean(ipp_t           *ipp,	/* I  - IPP message */
   3716               ipp_attribute_t **attr,	/* IO - IPP attribute */
   3717               int             element,	/* I  - Value number (0-based) */
   3718               int             boolvalue)/* I  - Boolean value */
   3719 {
   3720   _ipp_value_t	*value;			/* Current value */
   3721 
   3722 
   3723  /*
   3724   * Range check input...
   3725   */
   3726 
   3727   if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BOOLEAN ||
   3728       element < 0 || element > (*attr)->num_values)
   3729     return (0);
   3730 
   3731  /*
   3732   * Set the value and return...
   3733   */
   3734 
   3735   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   3736     value->boolean = (char)boolvalue;
   3737 
   3738   return (value != NULL);
   3739 }
   3740 
   3741 
   3742 /*
   3743  * 'ippSetCollection()' - Set a collection value in an attribute.
   3744  *
   3745  * The @code ipp@ parameter refers to an IPP message previously created using
   3746  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   3747  *
   3748  * The @code attr@ parameter may be modified as a result of setting the value.
   3749  *
   3750  * The @code element@ parameter specifies which value to set from 0 to
   3751  * @link ippGetCount(attr)@.
   3752  *
   3753  * @since CUPS 1.6/macOS 10.8@
   3754  */
   3755 
   3756 int					/* O  - 1 on success, 0 on failure */
   3757 ippSetCollection(
   3758     ipp_t           *ipp,		/* I  - IPP message */
   3759     ipp_attribute_t **attr,		/* IO - IPP attribute */
   3760     int             element,		/* I  - Value number (0-based) */
   3761     ipp_t           *colvalue)		/* I  - Collection value */
   3762 {
   3763   _ipp_value_t	*value;			/* Current value */
   3764 
   3765 
   3766  /*
   3767   * Range check input...
   3768   */
   3769 
   3770   if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BEGIN_COLLECTION ||
   3771       element < 0 || element > (*attr)->num_values || !colvalue)
   3772     return (0);
   3773 
   3774  /*
   3775   * Set the value and return...
   3776   */
   3777 
   3778   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   3779   {
   3780     if (value->collection)
   3781       ippDelete(value->collection);
   3782 
   3783     value->collection = colvalue;
   3784     colvalue->use ++;
   3785   }
   3786 
   3787   return (value != NULL);
   3788 }
   3789 
   3790 
   3791 /*
   3792  * 'ippSetDate()' - Set a date value in an attribute.
   3793  *
   3794  * The @code ipp@ parameter refers to an IPP message previously created using
   3795  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   3796  *
   3797  * The @code attr@ parameter may be modified as a result of setting the value.
   3798  *
   3799  * The @code element@ parameter specifies which value to set from 0 to
   3800  * @link ippGetCount(attr)@.
   3801  *
   3802  * @since CUPS 1.6/macOS 10.8@
   3803  */
   3804 
   3805 int					/* O  - 1 on success, 0 on failure */
   3806 ippSetDate(ipp_t             *ipp,	/* I  - IPP message */
   3807            ipp_attribute_t   **attr,	/* IO - IPP attribute */
   3808            int               element,	/* I  - Value number (0-based) */
   3809            const ipp_uchar_t *datevalue)/* I  - Date value */
   3810 {
   3811   _ipp_value_t	*value;			/* Current value */
   3812 
   3813 
   3814  /*
   3815   * Range check input...
   3816   */
   3817 
   3818   if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_DATE ||
   3819       element < 0 || element > (*attr)->num_values || !datevalue)
   3820     return (0);
   3821 
   3822  /*
   3823   * Set the value and return...
   3824   */
   3825 
   3826   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   3827     memcpy(value->date, datevalue, sizeof(value->date));
   3828 
   3829   return (value != NULL);
   3830 }
   3831 
   3832 
   3833 /*
   3834  * 'ippSetGroupTag()' - Set the group tag of an attribute.
   3835  *
   3836  * The @code ipp@ parameter refers to an IPP message previously created using
   3837  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   3838  *
   3839  * The @code attr@ parameter may be modified as a result of setting the value.
   3840  *
   3841  * The @code group@ parameter specifies the IPP attribute group tag: none
   3842  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
   3843  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
   3844  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
   3845  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
   3846  *
   3847  * @since CUPS 1.6/macOS 10.8@
   3848  */
   3849 
   3850 int					/* O  - 1 on success, 0 on failure */
   3851 ippSetGroupTag(
   3852     ipp_t           *ipp,		/* I  - IPP message */
   3853     ipp_attribute_t **attr,		/* IO - Attribute */
   3854     ipp_tag_t       group_tag)		/* I  - Group tag */
   3855 {
   3856  /*
   3857   * Range check input - group tag must be 0x01 to 0x0F, per RFC 2911...
   3858   */
   3859 
   3860   if (!ipp || !attr || !*attr ||
   3861       group_tag < IPP_TAG_ZERO || group_tag == IPP_TAG_END ||
   3862       group_tag >= IPP_TAG_UNSUPPORTED_VALUE)
   3863     return (0);
   3864 
   3865  /*
   3866   * Set the group tag and return...
   3867   */
   3868 
   3869   (*attr)->group_tag = group_tag;
   3870 
   3871   return (1);
   3872 }
   3873 
   3874 
   3875 /*
   3876  * 'ippSetInteger()' - Set an integer or enum value in an attribute.
   3877  *
   3878  * The @code ipp@ parameter refers to an IPP message previously created using
   3879  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   3880  *
   3881  * The @code attr@ parameter may be modified as a result of setting the value.
   3882  *
   3883  * The @code element@ parameter specifies which value to set from 0 to
   3884  * @link ippGetCount(attr)@.
   3885  *
   3886  * @since CUPS 1.6/macOS 10.8@
   3887  */
   3888 
   3889 int					/* O  - 1 on success, 0 on failure */
   3890 ippSetInteger(ipp_t           *ipp,	/* I  - IPP message */
   3891               ipp_attribute_t **attr,	/* IO - IPP attribute */
   3892               int             element,	/* I  - Value number (0-based) */
   3893               int             intvalue)	/* I  - Integer/enum value */
   3894 {
   3895   _ipp_value_t	*value;			/* Current value */
   3896 
   3897 
   3898  /*
   3899   * Range check input...
   3900   */
   3901 
   3902   if (!ipp || !attr || !*attr ||
   3903       ((*attr)->value_tag != IPP_TAG_INTEGER && (*attr)->value_tag != IPP_TAG_ENUM) ||
   3904       element < 0 || element > (*attr)->num_values)
   3905     return (0);
   3906 
   3907  /*
   3908   * Set the value and return...
   3909   */
   3910 
   3911   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   3912     value->integer = intvalue;
   3913 
   3914   return (value != NULL);
   3915 }
   3916 
   3917 
   3918 /*
   3919  * 'ippSetName()' - Set the name of an attribute.
   3920  *
   3921  * The @code ipp@ parameter refers to an IPP message previously created using
   3922  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   3923  *
   3924  * The @code attr@ parameter may be modified as a result of setting the value.
   3925  *
   3926  * @since CUPS 1.6/macOS 10.8@
   3927  */
   3928 
   3929 int					/* O  - 1 on success, 0 on failure */
   3930 ippSetName(ipp_t           *ipp,	/* I  - IPP message */
   3931 	   ipp_attribute_t **attr,	/* IO - IPP attribute */
   3932 	   const char      *name)	/* I  - Attribute name */
   3933 {
   3934   char	*temp;				/* Temporary name value */
   3935 
   3936 
   3937  /*
   3938   * Range check input...
   3939   */
   3940 
   3941   if (!ipp || !attr || !*attr)
   3942     return (0);
   3943 
   3944  /*
   3945   * Set the value and return...
   3946   */
   3947 
   3948   if ((temp = _cupsStrAlloc(name)) != NULL)
   3949   {
   3950     if ((*attr)->name)
   3951       _cupsStrFree((*attr)->name);
   3952 
   3953     (*attr)->name = temp;
   3954   }
   3955 
   3956   return (temp != NULL);
   3957 }
   3958 
   3959 
   3960 /*
   3961  * 'ippSetOctetString()' - Set an octetString value in an IPP attribute.
   3962  *
   3963  * The @code ipp@ parameter refers to an IPP message previously created using
   3964  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   3965  *
   3966  * The @code attr@ parameter may be modified as a result of setting the value.
   3967  *
   3968  * The @code element@ parameter specifies which value to set from 0 to
   3969  * @link ippGetCount(attr)@.
   3970  *
   3971  * @since CUPS 1.7/macOS 10.9@
   3972  */
   3973 
   3974 int					/* O  - 1 on success, 0 on failure */
   3975 ippSetOctetString(
   3976     ipp_t           *ipp,		/* I  - IPP message */
   3977     ipp_attribute_t **attr,		/* IO - IPP attribute */
   3978     int             element,		/* I  - Value number (0-based) */
   3979     const void      *data,		/* I  - Pointer to octetString data */
   3980     int             datalen)		/* I  - Length of octetString data */
   3981 {
   3982   _ipp_value_t	*value;			/* Current value */
   3983 
   3984 
   3985  /*
   3986   * Range check input...
   3987   */
   3988 
   3989   if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_STRING ||
   3990       element < 0 || element > (*attr)->num_values ||
   3991       datalen < 0 || datalen > IPP_MAX_LENGTH)
   3992     return (0);
   3993 
   3994  /*
   3995   * Set the value and return...
   3996   */
   3997 
   3998   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   3999   {
   4000     if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST)
   4001     {
   4002      /*
   4003       * Just copy the pointer...
   4004       */
   4005 
   4006       value->unknown.data   = (void *)data;
   4007       value->unknown.length = datalen;
   4008     }
   4009     else
   4010     {
   4011      /*
   4012       * Copy the data...
   4013       */
   4014 
   4015       if (value->unknown.data)
   4016       {
   4017        /*
   4018 	* Free previous data...
   4019 	*/
   4020 
   4021 	free(value->unknown.data);
   4022 
   4023 	value->unknown.data   = NULL;
   4024         value->unknown.length = 0;
   4025       }
   4026 
   4027       if (datalen > 0)
   4028       {
   4029 	void	*temp;			/* Temporary data pointer */
   4030 
   4031 	if ((temp = malloc((size_t)datalen)) != NULL)
   4032 	{
   4033 	  memcpy(temp, data, (size_t)datalen);
   4034 
   4035 	  value->unknown.data   = temp;
   4036 	  value->unknown.length = datalen;
   4037 	}
   4038 	else
   4039 	  return (0);
   4040       }
   4041     }
   4042   }
   4043 
   4044   return (value != NULL);
   4045 }
   4046 
   4047 
   4048 /*
   4049  * 'ippSetOperation()' - Set the operation ID in an IPP request message.
   4050  *
   4051  * The @code ipp@ parameter refers to an IPP message previously created using
   4052  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4053  *
   4054  * @since CUPS 1.6/macOS 10.8@
   4055  */
   4056 
   4057 int					/* O - 1 on success, 0 on failure */
   4058 ippSetOperation(ipp_t    *ipp,		/* I - IPP request message */
   4059                 ipp_op_t op)		/* I - Operation ID */
   4060 {
   4061  /*
   4062   * Range check input...
   4063   */
   4064 
   4065   if (!ipp)
   4066     return (0);
   4067 
   4068  /*
   4069   * Set the operation and return...
   4070   */
   4071 
   4072   ipp->request.op.operation_id = op;
   4073 
   4074   return (1);
   4075 }
   4076 
   4077 
   4078 /*
   4079  * 'ippSetRange()' - Set a rangeOfInteger value in an attribute.
   4080  *
   4081  * The @code ipp@ parameter refers to an IPP message previously created using
   4082  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4083  *
   4084  * The @code attr@ parameter may be modified as a result of setting the value.
   4085  *
   4086  * The @code element@ parameter specifies which value to set from 0 to
   4087  * @link ippGetCount(attr)@.
   4088  *
   4089  * @since CUPS 1.6/macOS 10.8@
   4090  */
   4091 
   4092 int					/* O  - 1 on success, 0 on failure */
   4093 ippSetRange(ipp_t           *ipp,	/* I  - IPP message */
   4094             ipp_attribute_t **attr,	/* IO - IPP attribute */
   4095             int             element,	/* I  - Value number (0-based) */
   4096 	    int             lowervalue,	/* I  - Lower bound for range */
   4097 	    int             uppervalue)	/* I  - Upper bound for range */
   4098 {
   4099   _ipp_value_t	*value;			/* Current value */
   4100 
   4101 
   4102  /*
   4103   * Range check input...
   4104   */
   4105 
   4106   if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_RANGE ||
   4107       element < 0 || element > (*attr)->num_values || lowervalue > uppervalue)
   4108     return (0);
   4109 
   4110  /*
   4111   * Set the value and return...
   4112   */
   4113 
   4114   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   4115   {
   4116     value->range.lower = lowervalue;
   4117     value->range.upper = uppervalue;
   4118   }
   4119 
   4120   return (value != NULL);
   4121 }
   4122 
   4123 
   4124 /*
   4125  * 'ippSetRequestId()' - Set the request ID in an IPP message.
   4126  *
   4127  * The @code ipp@ parameter refers to an IPP message previously created using
   4128  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4129  *
   4130  * The @code request_id@ parameter must be greater than 0.
   4131  *
   4132  * @since CUPS 1.6/macOS 10.8@
   4133  */
   4134 
   4135 int					/* O - 1 on success, 0 on failure */
   4136 ippSetRequestId(ipp_t *ipp,		/* I - IPP message */
   4137                 int   request_id)	/* I - Request ID */
   4138 {
   4139  /*
   4140   * Range check input; not checking request_id values since ipptool wants to send
   4141   * invalid values for conformance testing and a bad request_id does not affect the
   4142   * encoding of a message...
   4143   */
   4144 
   4145   if (!ipp)
   4146     return (0);
   4147 
   4148  /*
   4149   * Set the request ID and return...
   4150   */
   4151 
   4152   ipp->request.any.request_id = request_id;
   4153 
   4154   return (1);
   4155 }
   4156 
   4157 
   4158 /*
   4159  * 'ippSetResolution()' - Set a resolution value in an attribute.
   4160  *
   4161  * The @code ipp@ parameter refers to an IPP message previously created using
   4162  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4163  *
   4164  * The @code attr@ parameter may be modified as a result of setting the value.
   4165  *
   4166  * The @code element@ parameter specifies which value to set from 0 to
   4167  * @link ippGetCount(attr)@.
   4168  *
   4169  * @since CUPS 1.6/macOS 10.8@
   4170  */
   4171 
   4172 int					/* O  - 1 on success, 0 on failure */
   4173 ippSetResolution(
   4174     ipp_t           *ipp,		/* I  - IPP message */
   4175     ipp_attribute_t **attr,		/* IO - IPP attribute */
   4176     int             element,		/* I  - Value number (0-based) */
   4177     ipp_res_t       unitsvalue,		/* I  - Resolution units */
   4178     int             xresvalue,		/* I  - Horizontal/cross feed resolution */
   4179     int             yresvalue)		/* I  - Vertical/feed resolution */
   4180 {
   4181   _ipp_value_t	*value;			/* Current value */
   4182 
   4183 
   4184  /*
   4185   * Range check input...
   4186   */
   4187 
   4188   if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_RESOLUTION ||
   4189       element < 0 || element > (*attr)->num_values || xresvalue <= 0 || yresvalue <= 0 ||
   4190       unitsvalue < IPP_RES_PER_INCH || unitsvalue > IPP_RES_PER_CM)
   4191     return (0);
   4192 
   4193  /*
   4194   * Set the value and return...
   4195   */
   4196 
   4197   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   4198   {
   4199     value->resolution.units = unitsvalue;
   4200     value->resolution.xres  = xresvalue;
   4201     value->resolution.yres  = yresvalue;
   4202   }
   4203 
   4204   return (value != NULL);
   4205 }
   4206 
   4207 
   4208 /*
   4209  * 'ippSetState()' - Set the current state of the IPP message.
   4210  *
   4211  * @since CUPS 1.6/macOS 10.8@
   4212  */
   4213 
   4214 int					/* O - 1 on success, 0 on failure */
   4215 ippSetState(ipp_t       *ipp,		/* I - IPP message */
   4216             ipp_state_t state)		/* I - IPP state value */
   4217 {
   4218  /*
   4219   * Range check input...
   4220   */
   4221 
   4222   if (!ipp)
   4223     return (0);
   4224 
   4225  /*
   4226   * Set the state and return...
   4227   */
   4228 
   4229   ipp->state   = state;
   4230   ipp->current = NULL;
   4231 
   4232   return (1);
   4233 }
   4234 
   4235 
   4236 /*
   4237  * 'ippSetStatusCode()' - Set the status code in an IPP response or event message.
   4238  *
   4239  * The @code ipp@ parameter refers to an IPP message previously created using
   4240  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4241  *
   4242  * @since CUPS 1.6/macOS 10.8@
   4243  */
   4244 
   4245 int					/* O - 1 on success, 0 on failure */
   4246 ippSetStatusCode(ipp_t        *ipp,	/* I - IPP response or event message */
   4247                  ipp_status_t status)	/* I - Status code */
   4248 {
   4249  /*
   4250   * Range check input...
   4251   */
   4252 
   4253   if (!ipp)
   4254     return (0);
   4255 
   4256  /*
   4257   * Set the status code and return...
   4258   */
   4259 
   4260   ipp->request.status.status_code = status;
   4261 
   4262   return (1);
   4263 }
   4264 
   4265 
   4266 /*
   4267  * 'ippSetString()' - Set a string value in an attribute.
   4268  *
   4269  * The @code ipp@ parameter refers to an IPP message previously created using
   4270  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4271  *
   4272  * The @code attr@ parameter may be modified as a result of setting the value.
   4273  *
   4274  * The @code element@ parameter specifies which value to set from 0 to
   4275  * @link ippGetCount(attr)@.
   4276  *
   4277  * @since CUPS 1.6/macOS 10.8@
   4278  */
   4279 
   4280 int					/* O  - 1 on success, 0 on failure */
   4281 ippSetString(ipp_t           *ipp,	/* I  - IPP message */
   4282              ipp_attribute_t **attr,	/* IO - IPP attribute */
   4283              int             element,	/* I  - Value number (0-based) */
   4284 	     const char      *strvalue)	/* I  - String value */
   4285 {
   4286   char		*temp;			/* Temporary string */
   4287   _ipp_value_t	*value;			/* Current value */
   4288 
   4289 
   4290  /*
   4291   * Range check input...
   4292   */
   4293 
   4294   if (!ipp || !attr || !*attr ||
   4295       ((*attr)->value_tag != IPP_TAG_TEXTLANG &&
   4296       (*attr)->value_tag != IPP_TAG_NAMELANG &&
   4297        ((*attr)->value_tag < IPP_TAG_TEXT ||
   4298         (*attr)->value_tag > IPP_TAG_MIMETYPE)) ||
   4299       element < 0 || element > (*attr)->num_values || !strvalue)
   4300     return (0);
   4301 
   4302  /*
   4303   * Set the value and return...
   4304   */
   4305 
   4306   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
   4307   {
   4308     if (element > 0)
   4309       value->string.language = (*attr)->values[0].string.language;
   4310 
   4311     if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST)
   4312       value->string.text = (char *)strvalue;
   4313     else if ((temp = _cupsStrAlloc(strvalue)) != NULL)
   4314     {
   4315       if (value->string.text)
   4316         _cupsStrFree(value->string.text);
   4317 
   4318       value->string.text = temp;
   4319     }
   4320     else
   4321       return (0);
   4322   }
   4323 
   4324   return (value != NULL);
   4325 }
   4326 
   4327 
   4328 /*
   4329  * 'ippSetStringf()' - Set a formatted string value of an attribute.
   4330  *
   4331  * The @code ipp@ parameter refers to an IPP message previously created using
   4332  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4333  *
   4334  * The @code attr@ parameter may be modified as a result of setting the value.
   4335  *
   4336  * The @code element@ parameter specifies which value to set from 0 to
   4337  * @link ippGetCount(attr)@.
   4338  *
   4339  * The @code format@ parameter uses formatting characters compatible with the
   4340  * printf family of standard functions.  Additional arguments follow it as
   4341  * needed.  The formatted string is truncated as needed to the maximum length of
   4342  * the corresponding value type.
   4343  *
   4344  * @since CUPS 1.7/macOS 10.9@
   4345  */
   4346 
   4347 int					/* O  - 1 on success, 0 on failure */
   4348 ippSetStringf(ipp_t           *ipp,	/* I  - IPP message */
   4349               ipp_attribute_t **attr,	/* IO - IPP attribute */
   4350               int             element,	/* I  - Value number (0-based) */
   4351 	      const char      *format,	/* I  - Printf-style format string */
   4352 	      ...)			/* I  - Additional arguments as needed */
   4353 {
   4354   int		ret;			/* Return value */
   4355   va_list	ap;			/* Pointer to additional arguments */
   4356 
   4357 
   4358   va_start(ap, format);
   4359   ret = ippSetStringfv(ipp, attr, element, format, ap);
   4360   va_end(ap);
   4361 
   4362   return (ret);
   4363 }
   4364 
   4365 
   4366 /*
   4367  * 'ippSetStringf()' - Set a formatted string value of an attribute.
   4368  *
   4369  * The @code ipp@ parameter refers to an IPP message previously created using
   4370  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4371  *
   4372  * The @code attr@ parameter may be modified as a result of setting the value.
   4373  *
   4374  * The @code element@ parameter specifies which value to set from 0 to
   4375  * @link ippGetCount(attr)@.
   4376  *
   4377  * The @code format@ parameter uses formatting characters compatible with the
   4378  * printf family of standard functions.  Additional arguments follow it as
   4379  * needed.  The formatted string is truncated as needed to the maximum length of
   4380  * the corresponding value type.
   4381  *
   4382  * @since CUPS 1.7/macOS 10.9@
   4383  */
   4384 
   4385 int					/* O  - 1 on success, 0 on failure */
   4386 ippSetStringfv(ipp_t           *ipp,	/* I  - IPP message */
   4387                ipp_attribute_t **attr,	/* IO - IPP attribute */
   4388                int             element,	/* I  - Value number (0-based) */
   4389 	       const char      *format,	/* I  - Printf-style format string */
   4390 	       va_list         ap)	/* I  - Pointer to additional arguments */
   4391 {
   4392   ipp_tag_t	value_tag;		/* Value tag */
   4393   char		buffer[IPP_MAX_TEXT + 4];
   4394 					/* Formatted text string */
   4395   ssize_t	bytes,			/* Length of formatted value */
   4396 		max_bytes;		/* Maximum number of bytes for value */
   4397 
   4398 
   4399  /*
   4400   * Range check input...
   4401   */
   4402 
   4403   if (attr && *attr)
   4404     value_tag = (*attr)->value_tag & IPP_TAG_CUPS_MASK;
   4405   else
   4406     value_tag = IPP_TAG_ZERO;
   4407 
   4408   if (!ipp || !attr || !*attr ||
   4409       (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG &&
   4410        value_tag != IPP_TAG_NAMELANG) || value_tag > IPP_TAG_MIMETYPE ||
   4411       !format)
   4412     return (0);
   4413 
   4414  /*
   4415   * Format the string...
   4416   */
   4417 
   4418   if (!strcmp(format, "%s"))
   4419   {
   4420    /*
   4421     * Optimize the simple case...
   4422     */
   4423 
   4424     const char *s = va_arg(ap, char *);
   4425 
   4426     if (!s)
   4427       s = "(null)";
   4428 
   4429     bytes = (ssize_t)strlen(s);
   4430     strlcpy(buffer, s, sizeof(buffer));
   4431   }
   4432   else
   4433   {
   4434    /*
   4435     * Do a full formatting of the message...
   4436     */
   4437 
   4438     if ((bytes = vsnprintf(buffer, sizeof(buffer), format, ap)) < 0)
   4439       return (0);
   4440   }
   4441 
   4442  /*
   4443   * Limit the length of the string...
   4444   */
   4445 
   4446   switch (value_tag)
   4447   {
   4448     default :
   4449     case IPP_TAG_TEXT :
   4450     case IPP_TAG_TEXTLANG :
   4451         max_bytes = IPP_MAX_TEXT;
   4452         break;
   4453 
   4454     case IPP_TAG_NAME :
   4455     case IPP_TAG_NAMELANG :
   4456         max_bytes = IPP_MAX_NAME;
   4457         break;
   4458 
   4459     case IPP_TAG_CHARSET :
   4460         max_bytes = IPP_MAX_CHARSET;
   4461         break;
   4462 
   4463     case IPP_TAG_KEYWORD :
   4464         max_bytes = IPP_MAX_KEYWORD;
   4465         break;
   4466 
   4467     case IPP_TAG_LANGUAGE :
   4468         max_bytes = IPP_MAX_LANGUAGE;
   4469         break;
   4470 
   4471     case IPP_TAG_MIMETYPE :
   4472         max_bytes = IPP_MAX_MIMETYPE;
   4473         break;
   4474 
   4475     case IPP_TAG_URI :
   4476         max_bytes = IPP_MAX_URI;
   4477         break;
   4478 
   4479     case IPP_TAG_URISCHEME :
   4480         max_bytes = IPP_MAX_URISCHEME;
   4481         break;
   4482   }
   4483 
   4484   if (bytes >= max_bytes)
   4485   {
   4486     char	*bufmax,		/* Buffer at max_bytes */
   4487 		*bufptr;		/* Pointer into buffer */
   4488 
   4489     bufptr = buffer + strlen(buffer) - 1;
   4490     bufmax = buffer + max_bytes - 1;
   4491 
   4492     while (bufptr > bufmax)
   4493     {
   4494       if (*bufptr & 0x80)
   4495       {
   4496         while ((*bufptr & 0xc0) == 0x80 && bufptr > buffer)
   4497           bufptr --;
   4498       }
   4499 
   4500       bufptr --;
   4501     }
   4502 
   4503     *bufptr = '\0';
   4504   }
   4505 
   4506  /*
   4507   * Set the formatted string and return...
   4508   */
   4509 
   4510   return (ippSetString(ipp, attr, element, buffer));
   4511 }
   4512 
   4513 
   4514 /*
   4515  * 'ippSetValueTag()' - Set the value tag of an attribute.
   4516  *
   4517  * The @code ipp@ parameter refers to an IPP message previously created using
   4518  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4519  *
   4520  * The @code attr@ parameter may be modified as a result of setting the value.
   4521  *
   4522  * Integer (@code IPP_TAG_INTEGER@) values can be promoted to rangeOfInteger
   4523  * (@code IPP_TAG_RANGE@) values, the various string tags can be promoted to name
   4524  * (@code IPP_TAG_NAME@) or nameWithLanguage (@code IPP_TAG_NAMELANG@) values, text
   4525  * (@code IPP_TAG_TEXT@) values can be promoted to textWithLanguage
   4526  * (@code IPP_TAG_TEXTLANG@) values, and all values can be demoted to the various
   4527  * out-of-band value tags such as no-value (@code IPP_TAG_NOVALUE@). All other changes
   4528  * will be rejected.
   4529  *
   4530  * Promoting a string attribute to nameWithLanguage or textWithLanguage adds the language
   4531  * code in the "attributes-natural-language" attribute or, if not present, the language
   4532  * code for the current locale.
   4533  *
   4534  * @since CUPS 1.6/macOS 10.8@
   4535  */
   4536 
   4537 int					/* O  - 1 on success, 0 on failure */
   4538 ippSetValueTag(
   4539     ipp_t          *ipp,		/* I  - IPP message */
   4540     ipp_attribute_t **attr,		/* IO - IPP attribute */
   4541     ipp_tag_t       value_tag)		/* I  - Value tag */
   4542 {
   4543   int		i;			/* Looping var */
   4544   _ipp_value_t	*value;			/* Current value */
   4545   int		integer;		/* Current integer value */
   4546   cups_lang_t	*language;		/* Current language */
   4547   char		code[32];		/* Language code */
   4548   ipp_tag_t	temp_tag;		/* Temporary value tag */
   4549 
   4550 
   4551  /*
   4552   * Range check input...
   4553   */
   4554 
   4555   if (!ipp || !attr || !*attr)
   4556     return (0);
   4557 
   4558  /*
   4559   * If there is no change, return immediately...
   4560   */
   4561 
   4562   if (value_tag == (*attr)->value_tag)
   4563     return (1);
   4564 
   4565  /*
   4566   * Otherwise implement changes as needed...
   4567   */
   4568 
   4569   temp_tag = (ipp_tag_t)((int)((*attr)->value_tag) & IPP_TAG_CUPS_MASK);
   4570 
   4571   switch (value_tag)
   4572   {
   4573     case IPP_TAG_UNSUPPORTED_VALUE :
   4574     case IPP_TAG_DEFAULT :
   4575     case IPP_TAG_UNKNOWN :
   4576     case IPP_TAG_NOVALUE :
   4577     case IPP_TAG_NOTSETTABLE :
   4578     case IPP_TAG_DELETEATTR :
   4579     case IPP_TAG_ADMINDEFINE :
   4580        /*
   4581         * Free any existing values...
   4582         */
   4583 
   4584         if ((*attr)->num_values > 0)
   4585           ipp_free_values(*attr, 0, (*attr)->num_values);
   4586 
   4587        /*
   4588         * Set out-of-band value...
   4589         */
   4590 
   4591         (*attr)->value_tag = value_tag;
   4592         break;
   4593 
   4594     case IPP_TAG_RANGE :
   4595         if (temp_tag != IPP_TAG_INTEGER)
   4596           return (0);
   4597 
   4598         for (i = (*attr)->num_values, value = (*attr)->values;
   4599              i > 0;
   4600              i --, value ++)
   4601         {
   4602           integer            = value->integer;
   4603           value->range.lower = value->range.upper = integer;
   4604         }
   4605 
   4606         (*attr)->value_tag = IPP_TAG_RANGE;
   4607         break;
   4608 
   4609     case IPP_TAG_NAME :
   4610         if (temp_tag != IPP_TAG_KEYWORD && temp_tag != IPP_TAG_URI &&
   4611             temp_tag != IPP_TAG_URISCHEME && temp_tag != IPP_TAG_LANGUAGE &&
   4612             temp_tag != IPP_TAG_MIMETYPE)
   4613           return (0);
   4614 
   4615         (*attr)->value_tag = (ipp_tag_t)(IPP_TAG_NAME | ((*attr)->value_tag & IPP_TAG_CUPS_CONST));
   4616         break;
   4617 
   4618     case IPP_TAG_NAMELANG :
   4619     case IPP_TAG_TEXTLANG :
   4620         if (value_tag == IPP_TAG_NAMELANG &&
   4621             (temp_tag != IPP_TAG_NAME && temp_tag != IPP_TAG_KEYWORD &&
   4622              temp_tag != IPP_TAG_URI && temp_tag != IPP_TAG_URISCHEME &&
   4623              temp_tag != IPP_TAG_LANGUAGE && temp_tag != IPP_TAG_MIMETYPE))
   4624           return (0);
   4625 
   4626         if (value_tag == IPP_TAG_TEXTLANG && temp_tag != IPP_TAG_TEXT)
   4627           return (0);
   4628 
   4629         if (ipp->attrs && ipp->attrs->next && ipp->attrs->next->name &&
   4630             !strcmp(ipp->attrs->next->name, "attributes-natural-language"))
   4631         {
   4632          /*
   4633           * Use the language code from the IPP message...
   4634           */
   4635 
   4636 	  (*attr)->values[0].string.language =
   4637 	      _cupsStrAlloc(ipp->attrs->next->values[0].string.text);
   4638         }
   4639         else
   4640         {
   4641          /*
   4642           * Otherwise, use the language code corresponding to the locale...
   4643           */
   4644 
   4645 	  language = cupsLangDefault();
   4646 	  (*attr)->values[0].string.language = _cupsStrAlloc(ipp_lang_code(language->language,
   4647 									code,
   4648 									sizeof(code)));
   4649         }
   4650 
   4651         for (i = (*attr)->num_values - 1, value = (*attr)->values + 1;
   4652              i > 0;
   4653              i --, value ++)
   4654           value->string.language = (*attr)->values[0].string.language;
   4655 
   4656         if ((int)(*attr)->value_tag & IPP_TAG_CUPS_CONST)
   4657         {
   4658          /*
   4659           * Make copies of all values...
   4660           */
   4661 
   4662 	  for (i = (*attr)->num_values, value = (*attr)->values;
   4663 	       i > 0;
   4664 	       i --, value ++)
   4665 	    value->string.text = _cupsStrAlloc(value->string.text);
   4666         }
   4667 
   4668         (*attr)->value_tag = IPP_TAG_NAMELANG;
   4669         break;
   4670 
   4671     case IPP_TAG_KEYWORD :
   4672         if (temp_tag == IPP_TAG_NAME || temp_tag == IPP_TAG_NAMELANG)
   4673           break;			/* Silently "allow" name -> keyword */
   4674 
   4675     default :
   4676         return (0);
   4677   }
   4678 
   4679   return (1);
   4680 }
   4681 
   4682 
   4683 /*
   4684  * 'ippSetVersion()' - Set the version number in an IPP message.
   4685  *
   4686  * The @code ipp@ parameter refers to an IPP message previously created using
   4687  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
   4688  *
   4689  * The valid version numbers are currently 1.0, 1.1, 2.0, 2.1, and 2.2.
   4690  *
   4691  * @since CUPS 1.6/macOS 10.8@
   4692  */
   4693 
   4694 int					/* O - 1 on success, 0 on failure */
   4695 ippSetVersion(ipp_t *ipp,		/* I - IPP message */
   4696               int   major,		/* I - Major version number (major.minor) */
   4697               int   minor)		/* I - Minor version number (major.minor) */
   4698 {
   4699  /*
   4700   * Range check input...
   4701   */
   4702 
   4703   if (!ipp || major < 0 || minor < 0)
   4704     return (0);
   4705 
   4706  /*
   4707   * Set the version number...
   4708   */
   4709 
   4710   ipp->request.any.version[0] = (ipp_uchar_t)major;
   4711   ipp->request.any.version[1] = (ipp_uchar_t)minor;
   4712 
   4713   return (1);
   4714 }
   4715 
   4716 
   4717 /*
   4718  * 'ippTimeToDate()' - Convert from UNIX time to RFC 1903 format.
   4719  */
   4720 
   4721 const ipp_uchar_t *			/* O - RFC-1903 date/time data */
   4722 ippTimeToDate(time_t t)			/* I - UNIX time value */
   4723 {
   4724   struct tm	*unixdate;		/* UNIX unixdate/time info */
   4725   ipp_uchar_t	*date = _cupsGlobals()->ipp_date;
   4726 					/* RFC-1903 date/time data */
   4727 
   4728 
   4729  /*
   4730   * RFC-1903 date/time format is:
   4731   *
   4732   *    Byte(s)  Description
   4733   *    -------  -----------
   4734   *    0-1      Year (0 to 65535)
   4735   *    2        Month (1 to 12)
   4736   *    3        Day (1 to 31)
   4737   *    4        Hours (0 to 23)
   4738   *    5        Minutes (0 to 59)
   4739   *    6        Seconds (0 to 60, 60 = "leap second")
   4740   *    7        Deciseconds (0 to 9)
   4741   *    8        +/- UTC
   4742   *    9        UTC hours (0 to 11)
   4743   *    10       UTC minutes (0 to 59)
   4744   */
   4745 
   4746   unixdate = gmtime(&t);
   4747   unixdate->tm_year += 1900;
   4748 
   4749   date[0]  = (ipp_uchar_t)(unixdate->tm_year >> 8);
   4750   date[1]  = (ipp_uchar_t)(unixdate->tm_year);
   4751   date[2]  = (ipp_uchar_t)(unixdate->tm_mon + 1);
   4752   date[3]  = (ipp_uchar_t)unixdate->tm_mday;
   4753   date[4]  = (ipp_uchar_t)unixdate->tm_hour;
   4754   date[5]  = (ipp_uchar_t)unixdate->tm_min;
   4755   date[6]  = (ipp_uchar_t)unixdate->tm_sec;
   4756   date[7]  = 0;
   4757   date[8]  = '+';
   4758   date[9]  = 0;
   4759   date[10] = 0;
   4760 
   4761   return (date);
   4762 }
   4763 
   4764 
   4765 /*
   4766  * 'ippValidateAttribute()' - Validate the contents of an attribute.
   4767  *
   4768  * This function validates the contents of an attribute based on the name and
   4769  * value tag.  1 is returned if the attribute is valid, 0 otherwise.  On
   4770  * failure, cupsLastErrorString() is set to a human-readable message.
   4771  *
   4772  * @since CUPS 1.7/macOS 10.9@
   4773  */
   4774 
   4775 int					/* O - 1 if valid, 0 otherwise */
   4776 ippValidateAttribute(
   4777     ipp_attribute_t *attr)		/* I - Attribute */
   4778 {
   4779   int		i;			/* Looping var */
   4780   char		scheme[64],		/* Scheme from URI */
   4781 		userpass[256],		/* Username/password from URI */
   4782 		hostname[256],		/* Hostname from URI */
   4783 		resource[1024];		/* Resource from URI */
   4784   int		port,			/* Port number from URI */
   4785 		uri_status;		/* URI separation status */
   4786   const char	*ptr;			/* Pointer into string */
   4787   ipp_attribute_t *colattr;		/* Collection attribute */
   4788   regex_t	re;			/* Regular expression */
   4789   ipp_uchar_t	*date;			/* Current date value */
   4790   static const char * const uri_status_strings[] =
   4791   {					/* URI status strings */
   4792     "URI too large",
   4793     "Bad arguments to function",
   4794     "Bad resource in URI",
   4795     "Bad port number in URI",
   4796     "Bad hostname/address in URI",
   4797     "Bad username in URI",
   4798     "Bad scheme in URI",
   4799     "Bad/empty URI",
   4800     "OK",
   4801     "Missing scheme in URI",
   4802     "Unknown scheme in URI",
   4803     "Missing resource in URI"
   4804   };
   4805 
   4806 
   4807  /*
   4808   * Skip separators.
   4809   */
   4810 
   4811   if (!attr->name)
   4812     return (1);
   4813 
   4814  /*
   4815   * Validate the attribute name.
   4816   */
   4817 
   4818   for (ptr = attr->name; *ptr; ptr ++)
   4819     if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_')
   4820       break;
   4821 
   4822   if (*ptr || ptr == attr->name)
   4823   {
   4824     ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4825                   _("\"%s\": Bad attribute name - invalid character "
   4826 		    "(RFC 2911 section 4.1.3)."), attr->name);
   4827     return (0);
   4828   }
   4829 
   4830   if ((ptr - attr->name) > 255)
   4831   {
   4832     ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4833                   _("\"%s\": Bad attribute name - bad length %d "
   4834 		    "(RFC 2911 section 4.1.3)."), attr->name,
   4835 		  (int)(ptr - attr->name));
   4836     return (0);
   4837   }
   4838 
   4839   switch (attr->value_tag)
   4840   {
   4841     case IPP_TAG_INTEGER :
   4842         break;
   4843 
   4844     case IPP_TAG_BOOLEAN :
   4845         for (i = 0; i < attr->num_values; i ++)
   4846 	{
   4847 	  if (attr->values[i].boolean != 0 &&
   4848 	      attr->values[i].boolean != 1)
   4849 	  {
   4850 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4851                           _("\"%s\": Bad boolen value %d "
   4852 			    "(RFC 2911 section 4.1.11)."), attr->name,
   4853 			  attr->values[i].boolean);
   4854 	    return (0);
   4855 	  }
   4856 	}
   4857         break;
   4858 
   4859     case IPP_TAG_ENUM :
   4860         for (i = 0; i < attr->num_values; i ++)
   4861 	{
   4862 	  if (attr->values[i].integer < 1)
   4863 	  {
   4864 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4865 			  _("\"%s\": Bad enum value %d - out of range "
   4866 			    "(RFC 2911 section 4.1.4)."), attr->name,
   4867 			    attr->values[i].integer);
   4868             return (0);
   4869 	  }
   4870 	}
   4871         break;
   4872 
   4873     case IPP_TAG_STRING :
   4874         for (i = 0; i < attr->num_values; i ++)
   4875 	{
   4876 	  if (attr->values[i].unknown.length > IPP_MAX_OCTETSTRING)
   4877 	  {
   4878 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4879 			  _("\"%s\": Bad octetString value - bad length %d "
   4880 			    "(RFC 2911 section 4.1.10)."), attr->name,
   4881 			    attr->values[i].unknown.length);
   4882 	    return (0);
   4883 	  }
   4884 	}
   4885         break;
   4886 
   4887     case IPP_TAG_DATE :
   4888         for (i = 0; i < attr->num_values; i ++)
   4889 	{
   4890 	  date = attr->values[i].date;
   4891 
   4892           if (date[2] < 1 || date[2] > 12)
   4893 	  {
   4894 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4895 			  _("\"%s\": Bad dateTime month %u "
   4896 			    "(RFC 2911 section 4.1.14)."), attr->name, date[2]);
   4897 	    return (0);
   4898 	  }
   4899 
   4900           if (date[3] < 1 || date[3] > 31)
   4901 	  {
   4902 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4903 			  _("\"%s\": Bad dateTime day %u "
   4904 			    "(RFC 2911 section 4.1.14)."), attr->name, date[3]);
   4905 	    return (0);
   4906 	  }
   4907 
   4908           if (date[4] > 23)
   4909 	  {
   4910 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4911 			  _("\"%s\": Bad dateTime hours %u "
   4912 			    "(RFC 2911 section 4.1.14)."), attr->name, date[4]);
   4913 	    return (0);
   4914 	  }
   4915 
   4916           if (date[5] > 59)
   4917 	  {
   4918 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4919 			  _("\"%s\": Bad dateTime minutes %u "
   4920 			    "(RFC 2911 section 4.1.14)."), attr->name, date[5]);
   4921 	    return (0);
   4922 	  }
   4923 
   4924           if (date[6] > 60)
   4925 	  {
   4926 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4927 			  _("\"%s\": Bad dateTime seconds %u "
   4928 			    "(RFC 2911 section 4.1.14)."), attr->name, date[6]);
   4929 	    return (0);
   4930 	  }
   4931 
   4932           if (date[7] > 9)
   4933 	  {
   4934 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4935 			  _("\"%s\": Bad dateTime deciseconds %u "
   4936 			    "(RFC 2911 section 4.1.14)."), attr->name, date[7]);
   4937 	    return (0);
   4938 	  }
   4939 
   4940           if (date[8] != '-' && date[8] != '+')
   4941 	  {
   4942 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4943 			  _("\"%s\": Bad dateTime UTC sign '%c' "
   4944 			    "(RFC 2911 section 4.1.14)."), attr->name, date[8]);
   4945 	    return (0);
   4946 	  }
   4947 
   4948           if (date[9] > 11)
   4949 	  {
   4950 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4951 			  _("\"%s\": Bad dateTime UTC hours %u "
   4952 			    "(RFC 2911 section 4.1.14)."), attr->name, date[9]);
   4953 	    return (0);
   4954 	  }
   4955 
   4956           if (date[10] > 59)
   4957 	  {
   4958 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4959 			  _("\"%s\": Bad dateTime UTC minutes %u "
   4960 			    "(RFC 2911 section 4.1.14)."), attr->name, date[10]);
   4961 	    return (0);
   4962 	  }
   4963 	}
   4964         break;
   4965 
   4966     case IPP_TAG_RESOLUTION :
   4967         for (i = 0; i < attr->num_values; i ++)
   4968 	{
   4969 	  if (attr->values[i].resolution.xres <= 0)
   4970 	  {
   4971 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4972 			  _("\"%s\": Bad resolution value %dx%d%s - cross "
   4973 			    "feed resolution must be positive "
   4974 			    "(RFC 2911 section 4.1.15)."), attr->name,
   4975 			  attr->values[i].resolution.xres,
   4976 			  attr->values[i].resolution.yres,
   4977 			  attr->values[i].resolution.units ==
   4978 			      IPP_RES_PER_INCH ? "dpi" :
   4979 			      attr->values[i].resolution.units ==
   4980 				  IPP_RES_PER_CM ? "dpcm" : "unknown");
   4981 	    return (0);
   4982 	  }
   4983 
   4984 	  if (attr->values[i].resolution.yres <= 0)
   4985 	  {
   4986 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   4987 			  _("\"%s\": Bad resolution value %dx%d%s - feed "
   4988 			    "resolution must be positive "
   4989 			    "(RFC 2911 section 4.1.15)."), attr->name,
   4990 			  attr->values[i].resolution.xres,
   4991 			  attr->values[i].resolution.yres,
   4992 			  attr->values[i].resolution.units ==
   4993 			      IPP_RES_PER_INCH ? "dpi" :
   4994 			      attr->values[i].resolution.units ==
   4995 				  IPP_RES_PER_CM ? "dpcm" : "unknown");
   4996             return (0);
   4997 	  }
   4998 
   4999 	  if (attr->values[i].resolution.units != IPP_RES_PER_INCH &&
   5000 	      attr->values[i].resolution.units != IPP_RES_PER_CM)
   5001 	  {
   5002 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5003 			  _("\"%s\": Bad resolution value %dx%d%s - bad "
   5004 			    "units value (RFC 2911 section 4.1.15)."),
   5005 			  attr->name, attr->values[i].resolution.xres,
   5006 			  attr->values[i].resolution.yres,
   5007 			  attr->values[i].resolution.units ==
   5008 			      IPP_RES_PER_INCH ? "dpi" :
   5009 			      attr->values[i].resolution.units ==
   5010 				  IPP_RES_PER_CM ? "dpcm" : "unknown");
   5011 	    return (0);
   5012 	  }
   5013 	}
   5014         break;
   5015 
   5016     case IPP_TAG_RANGE :
   5017         for (i = 0; i < attr->num_values; i ++)
   5018 	{
   5019 	  if (attr->values[i].range.lower > attr->values[i].range.upper)
   5020 	  {
   5021 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5022 			  _("\"%s\": Bad rangeOfInteger value %d-%d - lower "
   5023 			    "greater than upper (RFC 2911 section 4.1.13)."),
   5024 			  attr->name, attr->values[i].range.lower,
   5025 			  attr->values[i].range.upper);
   5026 	    return (0);
   5027 	  }
   5028 	}
   5029         break;
   5030 
   5031     case IPP_TAG_BEGIN_COLLECTION :
   5032         for (i = 0; i < attr->num_values; i ++)
   5033 	{
   5034 	  for (colattr = attr->values[i].collection->attrs;
   5035 	       colattr;
   5036 	       colattr = colattr->next)
   5037 	  {
   5038 	    if (!ippValidateAttribute(colattr))
   5039 	      return (0);
   5040 	  }
   5041 	}
   5042         break;
   5043 
   5044     case IPP_TAG_TEXT :
   5045     case IPP_TAG_TEXTLANG :
   5046         for (i = 0; i < attr->num_values; i ++)
   5047 	{
   5048 	  for (ptr = attr->values[i].string.text; *ptr; ptr ++)
   5049 	  {
   5050 	    if ((*ptr & 0xe0) == 0xc0)
   5051 	    {
   5052 	      ptr ++;
   5053 	      if ((*ptr & 0xc0) != 0x80)
   5054 	        break;
   5055 	    }
   5056 	    else if ((*ptr & 0xf0) == 0xe0)
   5057 	    {
   5058 	      ptr ++;
   5059 	      if ((*ptr & 0xc0) != 0x80)
   5060 	        break;
   5061 	      ptr ++;
   5062 	      if ((*ptr & 0xc0) != 0x80)
   5063 	        break;
   5064 	    }
   5065 	    else if ((*ptr & 0xf8) == 0xf0)
   5066 	    {
   5067 	      ptr ++;
   5068 	      if ((*ptr & 0xc0) != 0x80)
   5069 	        break;
   5070 	      ptr ++;
   5071 	      if ((*ptr & 0xc0) != 0x80)
   5072 	        break;
   5073 	      ptr ++;
   5074 	      if ((*ptr & 0xc0) != 0x80)
   5075 	        break;
   5076 	    }
   5077 	    else if (*ptr & 0x80)
   5078 	      break;
   5079 	  }
   5080 
   5081 	  if (*ptr)
   5082 	  {
   5083 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5084 			  _("\"%s\": Bad text value \"%s\" - bad UTF-8 "
   5085 			    "sequence (RFC 2911 section 4.1.1)."), attr->name,
   5086 			  attr->values[i].string.text);
   5087 	    return (0);
   5088 	  }
   5089 
   5090 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_TEXT - 1))
   5091 	  {
   5092 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5093 			  _("\"%s\": Bad text value \"%s\" - bad length %d "
   5094 			    "(RFC 2911 section 4.1.1)."), attr->name,
   5095 			  attr->values[i].string.text,
   5096 			  (int)(ptr - attr->values[i].string.text));
   5097 	    return (0);
   5098 	  }
   5099 	}
   5100         break;
   5101 
   5102     case IPP_TAG_NAME :
   5103     case IPP_TAG_NAMELANG :
   5104         for (i = 0; i < attr->num_values; i ++)
   5105 	{
   5106 	  for (ptr = attr->values[i].string.text; *ptr; ptr ++)
   5107 	  {
   5108 	    if ((*ptr & 0xe0) == 0xc0)
   5109 	    {
   5110 	      ptr ++;
   5111 	      if ((*ptr & 0xc0) != 0x80)
   5112 	        break;
   5113 	    }
   5114 	    else if ((*ptr & 0xf0) == 0xe0)
   5115 	    {
   5116 	      ptr ++;
   5117 	      if ((*ptr & 0xc0) != 0x80)
   5118 	        break;
   5119 	      ptr ++;
   5120 	      if ((*ptr & 0xc0) != 0x80)
   5121 	        break;
   5122 	    }
   5123 	    else if ((*ptr & 0xf8) == 0xf0)
   5124 	    {
   5125 	      ptr ++;
   5126 	      if ((*ptr & 0xc0) != 0x80)
   5127 	        break;
   5128 	      ptr ++;
   5129 	      if ((*ptr & 0xc0) != 0x80)
   5130 	        break;
   5131 	      ptr ++;
   5132 	      if ((*ptr & 0xc0) != 0x80)
   5133 	        break;
   5134 	    }
   5135 	    else if (*ptr & 0x80)
   5136 	      break;
   5137 	  }
   5138 
   5139 	  if (*ptr)
   5140 	  {
   5141 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5142 			  _("\"%s\": Bad name value \"%s\" - bad UTF-8 "
   5143 			    "sequence (RFC 2911 section 4.1.2)."), attr->name,
   5144 			  attr->values[i].string.text);
   5145 	    return (0);
   5146 	  }
   5147 
   5148 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_NAME - 1))
   5149 	  {
   5150 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5151 			  _("\"%s\": Bad name value \"%s\" - bad length %d "
   5152 			    "(RFC 2911 section 4.1.2)."), attr->name,
   5153 			  attr->values[i].string.text,
   5154 			  (int)(ptr - attr->values[i].string.text));
   5155 	    return (0);
   5156 	  }
   5157 	}
   5158         break;
   5159 
   5160     case IPP_TAG_KEYWORD :
   5161         for (i = 0; i < attr->num_values; i ++)
   5162 	{
   5163 	  for (ptr = attr->values[i].string.text; *ptr; ptr ++)
   5164 	    if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' &&
   5165 	        *ptr != '_')
   5166 	      break;
   5167 
   5168 	  if (*ptr || ptr == attr->values[i].string.text)
   5169 	  {
   5170 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5171 			  _("\"%s\": Bad keyword value \"%s\" - invalid "
   5172 			    "character (RFC 2911 section 4.1.3)."),
   5173 			  attr->name, attr->values[i].string.text);
   5174 	    return (0);
   5175 	  }
   5176 
   5177 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_KEYWORD - 1))
   5178 	  {
   5179 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5180 			  _("\"%s\": Bad keyword value \"%s\" - bad "
   5181 			    "length %d (RFC 2911 section 4.1.3)."),
   5182 			  attr->name, attr->values[i].string.text,
   5183 			  (int)(ptr - attr->values[i].string.text));
   5184 	    return (0);
   5185 	  }
   5186 	}
   5187         break;
   5188 
   5189     case IPP_TAG_URI :
   5190         for (i = 0; i < attr->num_values; i ++)
   5191 	{
   5192 	  uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
   5193 	                               attr->values[i].string.text,
   5194 				       scheme, sizeof(scheme),
   5195 				       userpass, sizeof(userpass),
   5196 				       hostname, sizeof(hostname),
   5197 				       &port, resource, sizeof(resource));
   5198 
   5199 	  if (uri_status < HTTP_URI_STATUS_OK)
   5200 	  {
   5201 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5202 			  _("\"%s\": Bad URI value \"%s\" - %s "
   5203 			    "(RFC 2911 section 4.1.5)."), attr->name,
   5204 			  attr->values[i].string.text,
   5205 			  uri_status_strings[uri_status -
   5206 					     HTTP_URI_STATUS_OVERFLOW]);
   5207 	    return (0);
   5208 	  }
   5209 
   5210 	  if (strlen(attr->values[i].string.text) > (IPP_MAX_URI - 1))
   5211 	  {
   5212 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5213 			  _("\"%s\": Bad URI value \"%s\" - bad length %d "
   5214 			    "(RFC 2911 section 4.1.5)."), attr->name,
   5215 			  attr->values[i].string.text,
   5216 			  (int)strlen(attr->values[i].string.text));
   5217 	  }
   5218 	}
   5219         break;
   5220 
   5221     case IPP_TAG_URISCHEME :
   5222         for (i = 0; i < attr->num_values; i ++)
   5223 	{
   5224 	  ptr = attr->values[i].string.text;
   5225 	  if (islower(*ptr & 255))
   5226 	  {
   5227 	    for (ptr ++; *ptr; ptr ++)
   5228 	      if (!islower(*ptr & 255) && !isdigit(*ptr & 255) &&
   5229 	          *ptr != '+' && *ptr != '-' && *ptr != '.')
   5230                 break;
   5231 	  }
   5232 
   5233 	  if (*ptr || ptr == attr->values[i].string.text)
   5234 	  {
   5235 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5236 			  _("\"%s\": Bad uriScheme value \"%s\" - bad "
   5237 			    "characters (RFC 2911 section 4.1.6)."),
   5238 			  attr->name, attr->values[i].string.text);
   5239 	    return (0);
   5240 	  }
   5241 
   5242 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_URISCHEME - 1))
   5243 	  {
   5244 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5245 			  _("\"%s\": Bad uriScheme value \"%s\" - bad "
   5246 			    "length %d (RFC 2911 section 4.1.6)."),
   5247 			  attr->name, attr->values[i].string.text,
   5248 			  (int)(ptr - attr->values[i].string.text));
   5249 	    return (0);
   5250 	  }
   5251 	}
   5252         break;
   5253 
   5254     case IPP_TAG_CHARSET :
   5255         for (i = 0; i < attr->num_values; i ++)
   5256 	{
   5257 	  for (ptr = attr->values[i].string.text; *ptr; ptr ++)
   5258 	    if (!isprint(*ptr & 255) || isupper(*ptr & 255) ||
   5259 	        isspace(*ptr & 255))
   5260 	      break;
   5261 
   5262 	  if (*ptr || ptr == attr->values[i].string.text)
   5263 	  {
   5264 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5265 			  _("\"%s\": Bad charset value \"%s\" - bad "
   5266 			    "characters (RFC 2911 section 4.1.7)."),
   5267 			  attr->name, attr->values[i].string.text);
   5268 	    return (0);
   5269 	  }
   5270 
   5271 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_CHARSET - 1))
   5272 	  {
   5273 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5274 			  _("\"%s\": Bad charset value \"%s\" - bad "
   5275 			    "length %d (RFC 2911 section 4.1.7)."),
   5276 			  attr->name, attr->values[i].string.text,
   5277 			  (int)(ptr - attr->values[i].string.text));
   5278 	    return (0);
   5279 	  }
   5280 	}
   5281         break;
   5282 
   5283     case IPP_TAG_LANGUAGE :
   5284        /*
   5285         * The following regular expression is derived from the ABNF for
   5286 	* language tags in RFC 4646.  All I can say is that this is the
   5287 	* easiest way to check the values...
   5288 	*/
   5289 
   5290         if ((i = regcomp(&re,
   5291 			 "^("
   5292 			 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
   5293 								/* language */
   5294 			 "(-[a-z][a-z][a-z][a-z]){0,1}"		/* script */
   5295 			 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}"	/* region */
   5296 			 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*"	/* variant */
   5297 			 "(-[a-wy-z](-[a-z0-9]{2,8})+)*"	/* extension */
   5298 			 "(-x(-[a-z0-9]{1,8})+)*"		/* privateuse */
   5299 			 "|"
   5300 			 "x(-[a-z0-9]{1,8})+"			/* privateuse */
   5301 			 "|"
   5302 			 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}"	/* grandfathered */
   5303 			 ")$",
   5304 			 REG_NOSUB | REG_EXTENDED)) != 0)
   5305         {
   5306           char	temp[256];		/* Temporary error string */
   5307 
   5308           regerror(i, &re, temp, sizeof(temp));
   5309 	  ipp_set_error(IPP_STATUS_ERROR_INTERNAL,
   5310 			_("Unable to compile naturalLanguage regular "
   5311 			  "expression: %s."), temp);
   5312 	  return (0);
   5313         }
   5314 
   5315         for (i = 0; i < attr->num_values; i ++)
   5316 	{
   5317 	  if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
   5318 	  {
   5319 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5320 			  _("\"%s\": Bad naturalLanguage value \"%s\" - bad "
   5321 			    "characters (RFC 2911 section 4.1.8)."),
   5322 			  attr->name, attr->values[i].string.text);
   5323 	    regfree(&re);
   5324 	    return (0);
   5325 	  }
   5326 
   5327 	  if (strlen(attr->values[i].string.text) > (IPP_MAX_LANGUAGE - 1))
   5328 	  {
   5329 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5330 			  _("\"%s\": Bad naturalLanguage value \"%s\" - bad "
   5331 			    "length %d (RFC 2911 section 4.1.8)."),
   5332 			  attr->name, attr->values[i].string.text,
   5333 			  (int)strlen(attr->values[i].string.text));
   5334 	    regfree(&re);
   5335 	    return (0);
   5336 	  }
   5337 	}
   5338 
   5339 	regfree(&re);
   5340         break;
   5341 
   5342     case IPP_TAG_MIMETYPE :
   5343        /*
   5344         * The following regular expression is derived from the ABNF for
   5345 	* MIME media types in RFC 2045 and 4288.  All I can say is that this is
   5346 	* the easiest way to check the values...
   5347 	*/
   5348 
   5349         if ((i = regcomp(&re,
   5350 			 "^"
   5351 			 "[-a-zA-Z0-9!#$&.+^_]{1,127}"		/* type-name */
   5352 			 "/"
   5353 			 "[-a-zA-Z0-9!#$&.+^_]{1,127}"		/* subtype-name */
   5354 			 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}="	/* parameter= */
   5355 			 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
   5356 			 					/* value */
   5357 			 "$",
   5358 			 REG_NOSUB | REG_EXTENDED)) != 0)
   5359         {
   5360           char	temp[256];		/* Temporary error string */
   5361 
   5362           regerror(i, &re, temp, sizeof(temp));
   5363 	  ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5364 			_("Unable to compile mimeMediaType regular "
   5365 			  "expression: %s."), temp);
   5366 	  return (0);
   5367         }
   5368 
   5369         for (i = 0; i < attr->num_values; i ++)
   5370 	{
   5371 	  if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
   5372 	  {
   5373 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5374 			  _("\"%s\": Bad mimeMediaType value \"%s\" - bad "
   5375 			    "characters (RFC 2911 section 4.1.9)."),
   5376 			  attr->name, attr->values[i].string.text);
   5377 	    regfree(&re);
   5378 	    return (0);
   5379 	  }
   5380 
   5381 	  if (strlen(attr->values[i].string.text) > (IPP_MAX_MIMETYPE - 1))
   5382 	  {
   5383 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
   5384 			  _("\"%s\": Bad mimeMediaType value \"%s\" - bad "
   5385 			    "length %d (RFC 2911 section 4.1.9)."),
   5386 			  attr->name, attr->values[i].string.text,
   5387 			  (int)strlen(attr->values[i].string.text));
   5388 	    regfree(&re);
   5389 	    return (0);
   5390 	  }
   5391 	}
   5392 
   5393 	regfree(&re);
   5394         break;
   5395 
   5396     default :
   5397         break;
   5398   }
   5399 
   5400   return (1);
   5401 }
   5402 
   5403 
   5404 /*
   5405  * 'ippValidateAttributes()' - Validate all attributes in an IPP message.
   5406  *
   5407  * This function validates the contents of the IPP message, including each
   5408  * attribute.  Like @link ippValidateAttribute@, cupsLastErrorString() is set
   5409  * to a human-readable message on failure.
   5410  *
   5411  * @since CUPS 1.7/macOS 10.9@
   5412  */
   5413 
   5414 int					/* O - 1 if valid, 0 otherwise */
   5415 ippValidateAttributes(ipp_t *ipp)	/* I - IPP message */
   5416 {
   5417   ipp_attribute_t	*attr;		/* Current attribute */
   5418 
   5419 
   5420   if (!ipp)
   5421     return (1);
   5422 
   5423   for (attr = ipp->attrs; attr; attr = attr->next)
   5424     if (!ippValidateAttribute(attr))
   5425       return (0);
   5426 
   5427   return (1);
   5428 }
   5429 
   5430 
   5431 /*
   5432  * 'ippWrite()' - Write data for an IPP message to a HTTP connection.
   5433  */
   5434 
   5435 ipp_state_t				/* O - Current state */
   5436 ippWrite(http_t *http,			/* I - HTTP connection */
   5437          ipp_t  *ipp)			/* I - IPP data */
   5438 {
   5439   DEBUG_printf(("ippWrite(http=%p, ipp=%p)", (void *)http, (void *)ipp));
   5440 
   5441   if (!http)
   5442     return (IPP_STATE_ERROR);
   5443 
   5444   return (ippWriteIO(http, (ipp_iocb_t)httpWrite2, http->blocking, NULL, ipp));
   5445 }
   5446 
   5447 
   5448 /*
   5449  * 'ippWriteFile()' - Write data for an IPP message to a file.
   5450  *
   5451  * @since CUPS 1.1.19/macOS 10.3@
   5452  */
   5453 
   5454 ipp_state_t				/* O - Current state */
   5455 ippWriteFile(int   fd,			/* I - HTTP data */
   5456              ipp_t *ipp)		/* I - IPP data */
   5457 {
   5458   DEBUG_printf(("ippWriteFile(fd=%d, ipp=%p)", fd, (void *)ipp));
   5459 
   5460   ipp->state = IPP_STATE_IDLE;
   5461 
   5462   return (ippWriteIO(&fd, (ipp_iocb_t)ipp_write_file, 1, NULL, ipp));
   5463 }
   5464 
   5465 
   5466 /*
   5467  * 'ippWriteIO()' - Write data for an IPP message.
   5468  *
   5469  * @since CUPS 1.2/macOS 10.5@
   5470  */
   5471 
   5472 ipp_state_t				/* O - Current state */
   5473 ippWriteIO(void       *dst,		/* I - Destination */
   5474            ipp_iocb_t cb,		/* I - Write callback function */
   5475 	   int        blocking,		/* I - Use blocking IO? */
   5476 	   ipp_t      *parent,		/* I - Parent IPP message */
   5477            ipp_t      *ipp)		/* I - IPP data */
   5478 {
   5479   int			i;		/* Looping var */
   5480   int			n;		/* Length of data */
   5481   unsigned char		*buffer,	/* Data buffer */
   5482 			*bufptr;	/* Pointer into buffer */
   5483   ipp_attribute_t	*attr;		/* Current attribute */
   5484   _ipp_value_t		*value;		/* Current value */
   5485 
   5486 
   5487   DEBUG_printf(("ippWriteIO(dst=%p, cb=%p, blocking=%d, parent=%p, ipp=%p)", (void *)dst, (void *)cb, blocking, (void *)parent, (void *)ipp));
   5488 
   5489   if (!dst || !ipp)
   5490     return (IPP_STATE_ERROR);
   5491 
   5492   if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL)
   5493   {
   5494     DEBUG_puts("1ippWriteIO: Unable to get write buffer");
   5495     return (IPP_STATE_ERROR);
   5496   }
   5497 
   5498   switch (ipp->state)
   5499   {
   5500     case IPP_STATE_IDLE :
   5501         ipp->state ++; /* Avoid common problem... */
   5502 
   5503     case IPP_STATE_HEADER :
   5504         if (parent == NULL)
   5505 	{
   5506 	 /*
   5507 	  * Send the request header:
   5508 	  *
   5509 	  *                 Version = 2 bytes
   5510 	  *   Operation/Status Code = 2 bytes
   5511 	  *              Request ID = 4 bytes
   5512 	  *                   Total = 8 bytes
   5513 	  */
   5514 
   5515           bufptr = buffer;
   5516 
   5517 	  *bufptr++ = ipp->request.any.version[0];
   5518 	  *bufptr++ = ipp->request.any.version[1];
   5519 	  *bufptr++ = (ipp_uchar_t)(ipp->request.any.op_status >> 8);
   5520 	  *bufptr++ = (ipp_uchar_t)ipp->request.any.op_status;
   5521 	  *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 24);
   5522 	  *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 16);
   5523 	  *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 8);
   5524 	  *bufptr++ = (ipp_uchar_t)ipp->request.any.request_id;
   5525 
   5526 	  DEBUG_printf(("2ippWriteIO: version=%d.%d", buffer[0], buffer[1]));
   5527 	  DEBUG_printf(("2ippWriteIO: op_status=%04x",
   5528 			ipp->request.any.op_status));
   5529 	  DEBUG_printf(("2ippWriteIO: request_id=%d",
   5530 			ipp->request.any.request_id));
   5531 
   5532           if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5533 	  {
   5534 	    DEBUG_puts("1ippWriteIO: Could not write IPP header...");
   5535 	    _cupsBufferRelease((char *)buffer);
   5536 	    return (IPP_STATE_ERROR);
   5537 	  }
   5538 	}
   5539 
   5540        /*
   5541 	* Reset the state engine to point to the first attribute
   5542 	* in the request/response, with no current group.
   5543 	*/
   5544 
   5545         ipp->state   = IPP_STATE_ATTRIBUTE;
   5546 	ipp->current = ipp->attrs;
   5547 	ipp->curtag  = IPP_TAG_ZERO;
   5548 
   5549 	DEBUG_printf(("1ippWriteIO: ipp->current=%p", (void *)ipp->current));
   5550 
   5551        /*
   5552         * If blocking is disabled, stop here...
   5553 	*/
   5554 
   5555         if (!blocking)
   5556 	  break;
   5557 
   5558     case IPP_STATE_ATTRIBUTE :
   5559         while (ipp->current != NULL)
   5560 	{
   5561 	 /*
   5562 	  * Write this attribute...
   5563 	  */
   5564 
   5565 	  bufptr = buffer;
   5566 	  attr   = ipp->current;
   5567 
   5568 	  ipp->current = ipp->current->next;
   5569 
   5570           if (!parent)
   5571 	  {
   5572 	    if (ipp->curtag != attr->group_tag)
   5573 	    {
   5574 	     /*
   5575 	      * Send a group tag byte...
   5576 	      */
   5577 
   5578 	      ipp->curtag = attr->group_tag;
   5579 
   5580 	      if (attr->group_tag == IPP_TAG_ZERO)
   5581 		continue;
   5582 
   5583 	      DEBUG_printf(("2ippWriteIO: wrote group tag=%x(%s)",
   5584 			    attr->group_tag, ippTagString(attr->group_tag)));
   5585 	      *bufptr++ = (ipp_uchar_t)attr->group_tag;
   5586 	    }
   5587 	    else if (attr->group_tag == IPP_TAG_ZERO)
   5588 	      continue;
   5589 	  }
   5590 
   5591 	  DEBUG_printf(("1ippWriteIO: %s (%s%s)", attr->name,
   5592 	                attr->num_values > 1 ? "1setOf " : "",
   5593 			ippTagString(attr->value_tag)));
   5594 
   5595          /*
   5596 	  * Write the attribute tag and name.
   5597 	  *
   5598 	  * The attribute name length does not include the trailing nul
   5599 	  * character in the source string.
   5600 	  *
   5601 	  * Collection values (parent != NULL) are written differently...
   5602 	  */
   5603 
   5604           if (parent == NULL)
   5605 	  {
   5606            /*
   5607 	    * Get the length of the attribute name, and make sure it won't
   5608 	    * overflow the buffer...
   5609 	    */
   5610 
   5611             if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 8))
   5612 	    {
   5613 	      DEBUG_printf(("1ippWriteIO: Attribute name too long (%d)", n));
   5614 	      _cupsBufferRelease((char *)buffer);
   5615 	      return (IPP_STATE_ERROR);
   5616 	    }
   5617 
   5618            /*
   5619 	    * Write the value tag, name length, and name string...
   5620 	    */
   5621 
   5622             DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
   5623 	                  attr->value_tag, ippTagString(attr->value_tag)));
   5624             DEBUG_printf(("2ippWriteIO: writing name=%d,\"%s\"", n,
   5625 	                  attr->name));
   5626 
   5627             if (attr->value_tag > 0xff)
   5628             {
   5629               *bufptr++ = IPP_TAG_EXTENSION;
   5630 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 24);
   5631 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 16);
   5632 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 8);
   5633 	      *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5634             }
   5635             else
   5636 	      *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5637 
   5638 	    *bufptr++ = (ipp_uchar_t)(n >> 8);
   5639 	    *bufptr++ = (ipp_uchar_t)n;
   5640 	    memcpy(bufptr, attr->name, (size_t)n);
   5641 	    bufptr += n;
   5642           }
   5643 	  else
   5644 	  {
   5645            /*
   5646 	    * Get the length of the attribute name, and make sure it won't
   5647 	    * overflow the buffer...
   5648 	    */
   5649 
   5650             if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 12))
   5651 	    {
   5652 	      DEBUG_printf(("1ippWriteIO: Attribute name too long (%d)", n));
   5653 	      _cupsBufferRelease((char *)buffer);
   5654 	      return (IPP_STATE_ERROR);
   5655 	    }
   5656 
   5657            /*
   5658 	    * Write the member name tag, name length, name string, value tag,
   5659 	    * and empty name for the collection member attribute...
   5660 	    */
   5661 
   5662             DEBUG_printf(("2ippWriteIO: writing value tag=%x(memberName)",
   5663 	                  IPP_TAG_MEMBERNAME));
   5664             DEBUG_printf(("2ippWriteIO: writing name=%d,\"%s\"", n,
   5665 	                  attr->name));
   5666             DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
   5667 	                  attr->value_tag, ippTagString(attr->value_tag)));
   5668             DEBUG_puts("2ippWriteIO: writing name=0,\"\"");
   5669 
   5670             *bufptr++ = IPP_TAG_MEMBERNAME;
   5671 	    *bufptr++ = 0;
   5672 	    *bufptr++ = 0;
   5673 	    *bufptr++ = (ipp_uchar_t)(n >> 8);
   5674 	    *bufptr++ = (ipp_uchar_t)n;
   5675 	    memcpy(bufptr, attr->name, (size_t)n);
   5676 	    bufptr += n;
   5677 
   5678             if (attr->value_tag > 0xff)
   5679             {
   5680               *bufptr++ = IPP_TAG_EXTENSION;
   5681 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 24);
   5682 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 16);
   5683 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 8);
   5684 	      *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5685             }
   5686             else
   5687 	      *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5688 
   5689             *bufptr++ = 0;
   5690             *bufptr++ = 0;
   5691 	  }
   5692 
   5693          /*
   5694 	  * Now write the attribute value(s)...
   5695 	  */
   5696 
   5697 	  switch (attr->value_tag & ~IPP_TAG_CUPS_CONST)
   5698 	  {
   5699 	    case IPP_TAG_UNSUPPORTED_VALUE :
   5700 	    case IPP_TAG_DEFAULT :
   5701 	    case IPP_TAG_UNKNOWN :
   5702 	    case IPP_TAG_NOVALUE :
   5703 	    case IPP_TAG_NOTSETTABLE :
   5704 	    case IPP_TAG_DELETEATTR :
   5705 	    case IPP_TAG_ADMINDEFINE :
   5706 		*bufptr++ = 0;
   5707 		*bufptr++ = 0;
   5708 	        break;
   5709 
   5710 	    case IPP_TAG_INTEGER :
   5711 	    case IPP_TAG_ENUM :
   5712 	        for (i = 0, value = attr->values;
   5713 		     i < attr->num_values;
   5714 		     i ++, value ++)
   5715 		{
   5716                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 9)
   5717 		  {
   5718                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5719 	            {
   5720 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   5721 		                 "attribute...");
   5722 		      _cupsBufferRelease((char *)buffer);
   5723 	              return (IPP_STATE_ERROR);
   5724 	            }
   5725 
   5726 		    bufptr = buffer;
   5727 		  }
   5728 
   5729 		  if (i)
   5730 		  {
   5731 		   /*
   5732 		    * Arrays and sets are done by sending additional
   5733 		    * values with a zero-length name...
   5734 		    */
   5735 
   5736                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5737 		    *bufptr++ = 0;
   5738 		    *bufptr++ = 0;
   5739 		  }
   5740 
   5741 		 /*
   5742 	          * Integers and enumerations are both 4-byte signed
   5743 		  * (twos-complement) values.
   5744 		  *
   5745 		  * Put the 2-byte length and 4-byte value into the buffer...
   5746 		  */
   5747 
   5748 	          *bufptr++ = 0;
   5749 		  *bufptr++ = 4;
   5750 		  *bufptr++ = (ipp_uchar_t)(value->integer >> 24);
   5751 		  *bufptr++ = (ipp_uchar_t)(value->integer >> 16);
   5752 		  *bufptr++ = (ipp_uchar_t)(value->integer >> 8);
   5753 		  *bufptr++ = (ipp_uchar_t)value->integer;
   5754 		}
   5755 		break;
   5756 
   5757 	    case IPP_TAG_BOOLEAN :
   5758 	        for (i = 0, value = attr->values;
   5759 		     i < attr->num_values;
   5760 		     i ++, value ++)
   5761 		{
   5762                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 6)
   5763 		  {
   5764                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5765 	            {
   5766 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   5767 		                 "attribute...");
   5768 		      _cupsBufferRelease((char *)buffer);
   5769 	              return (IPP_STATE_ERROR);
   5770 	            }
   5771 
   5772 		    bufptr = buffer;
   5773 		  }
   5774 
   5775 		  if (i)
   5776 		  {
   5777 		   /*
   5778 		    * Arrays and sets are done by sending additional
   5779 		    * values with a zero-length name...
   5780 		    */
   5781 
   5782                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5783 		    *bufptr++ = 0;
   5784 		    *bufptr++ = 0;
   5785 		  }
   5786 
   5787                  /*
   5788 		  * Boolean values are 1-byte; 0 = false, 1 = true.
   5789 		  *
   5790 		  * Put the 2-byte length and 1-byte value into the buffer...
   5791 		  */
   5792 
   5793 	          *bufptr++ = 0;
   5794 		  *bufptr++ = 1;
   5795 		  *bufptr++ = (ipp_uchar_t)value->boolean;
   5796 		}
   5797 		break;
   5798 
   5799 	    case IPP_TAG_TEXT :
   5800 	    case IPP_TAG_NAME :
   5801 	    case IPP_TAG_KEYWORD :
   5802 	    case IPP_TAG_URI :
   5803 	    case IPP_TAG_URISCHEME :
   5804 	    case IPP_TAG_CHARSET :
   5805 	    case IPP_TAG_LANGUAGE :
   5806 	    case IPP_TAG_MIMETYPE :
   5807 	        for (i = 0, value = attr->values;
   5808 		     i < attr->num_values;
   5809 		     i ++, value ++)
   5810 		{
   5811 		  if (i)
   5812 		  {
   5813 		   /*
   5814 		    * Arrays and sets are done by sending additional
   5815 		    * values with a zero-length name...
   5816 		    */
   5817 
   5818         	    DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
   5819 		                  attr->value_tag,
   5820 				  ippTagString(attr->value_tag)));
   5821         	    DEBUG_printf(("2ippWriteIO: writing name=0,\"\""));
   5822 
   5823                     if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
   5824 		    {
   5825                       if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5826 	              {
   5827 	        	DEBUG_puts("1ippWriteIO: Could not write IPP "
   5828 			           "attribute...");
   5829 			_cupsBufferRelease((char *)buffer);
   5830 	        	return (IPP_STATE_ERROR);
   5831 	              }
   5832 
   5833 		      bufptr = buffer;
   5834 		    }
   5835 
   5836                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5837 		    *bufptr++ = 0;
   5838 		    *bufptr++ = 0;
   5839 		  }
   5840 
   5841                   if (value->string.text != NULL)
   5842                     n = (int)strlen(value->string.text);
   5843 		  else
   5844 		    n = 0;
   5845 
   5846                   if (n > (IPP_BUF_SIZE - 2))
   5847 		  {
   5848 		    DEBUG_printf(("1ippWriteIO: String too long (%d)", n));
   5849 		    _cupsBufferRelease((char *)buffer);
   5850 		    return (IPP_STATE_ERROR);
   5851 		  }
   5852 
   5853                   DEBUG_printf(("2ippWriteIO: writing string=%d,\"%s\"", n,
   5854 		                value->string.text));
   5855 
   5856                   if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
   5857 		  {
   5858                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5859 	            {
   5860 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   5861 		                 "attribute...");
   5862 		      _cupsBufferRelease((char *)buffer);
   5863 	              return (IPP_STATE_ERROR);
   5864 	            }
   5865 
   5866 		    bufptr = buffer;
   5867 		  }
   5868 
   5869 		 /*
   5870 		  * All simple strings consist of the 2-byte length and
   5871 		  * character data without the trailing nul normally found
   5872 		  * in C strings.  Also, strings cannot be longer than IPP_MAX_LENGTH
   5873 		  * bytes since the 2-byte length is a signed (twos-complement)
   5874 		  * value.
   5875 		  *
   5876 		  * Put the 2-byte length and string characters in the buffer.
   5877 		  */
   5878 
   5879 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
   5880 		  *bufptr++ = (ipp_uchar_t)n;
   5881 
   5882 		  if (n > 0)
   5883 		  {
   5884 		    memcpy(bufptr, value->string.text, (size_t)n);
   5885 		    bufptr += n;
   5886 		  }
   5887 		}
   5888 		break;
   5889 
   5890 	    case IPP_TAG_DATE :
   5891 	        for (i = 0, value = attr->values;
   5892 		     i < attr->num_values;
   5893 		     i ++, value ++)
   5894 		{
   5895                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 16)
   5896 		  {
   5897                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5898 	            {
   5899 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   5900 		                 "attribute...");
   5901 		      _cupsBufferRelease((char *)buffer);
   5902 	              return (IPP_STATE_ERROR);
   5903 	            }
   5904 
   5905 		    bufptr = buffer;
   5906 		  }
   5907 
   5908 		  if (i)
   5909 		  {
   5910 		   /*
   5911 		    * Arrays and sets are done by sending additional
   5912 		    * values with a zero-length name...
   5913 		    */
   5914 
   5915                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5916 		    *bufptr++ = 0;
   5917 		    *bufptr++ = 0;
   5918 		  }
   5919 
   5920                  /*
   5921 		  * Date values consist of a 2-byte length and an
   5922 		  * 11-byte date/time structure defined by RFC 1903.
   5923 		  *
   5924 		  * Put the 2-byte length and 11-byte date/time
   5925 		  * structure in the buffer.
   5926 		  */
   5927 
   5928 	          *bufptr++ = 0;
   5929 		  *bufptr++ = 11;
   5930 		  memcpy(bufptr, value->date, 11);
   5931 		  bufptr += 11;
   5932 		}
   5933 		break;
   5934 
   5935 	    case IPP_TAG_RESOLUTION :
   5936 	        for (i = 0, value = attr->values;
   5937 		     i < attr->num_values;
   5938 		     i ++, value ++)
   5939 		{
   5940                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 14)
   5941 		  {
   5942                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5943 	            {
   5944 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   5945 		                 "attribute...");
   5946 		      _cupsBufferRelease((char *)buffer);
   5947 		      return (IPP_STATE_ERROR);
   5948 	            }
   5949 
   5950 		    bufptr = buffer;
   5951 		  }
   5952 
   5953 		  if (i)
   5954 		  {
   5955 		   /*
   5956 		    * Arrays and sets are done by sending additional
   5957 		    * values with a zero-length name...
   5958 		    */
   5959 
   5960                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   5961 		    *bufptr++ = 0;
   5962 		    *bufptr++ = 0;
   5963 		  }
   5964 
   5965                  /*
   5966 		  * Resolution values consist of a 2-byte length,
   5967 		  * 4-byte horizontal resolution value, 4-byte vertical
   5968 		  * resolution value, and a 1-byte units value.
   5969 		  *
   5970 		  * Put the 2-byte length and resolution value data
   5971 		  * into the buffer.
   5972 		  */
   5973 
   5974 	          *bufptr++ = 0;
   5975 		  *bufptr++ = 9;
   5976 		  *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 24);
   5977 		  *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 16);
   5978 		  *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 8);
   5979 		  *bufptr++ = (ipp_uchar_t)value->resolution.xres;
   5980 		  *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 24);
   5981 		  *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 16);
   5982 		  *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 8);
   5983 		  *bufptr++ = (ipp_uchar_t)value->resolution.yres;
   5984 		  *bufptr++ = (ipp_uchar_t)value->resolution.units;
   5985 		}
   5986 		break;
   5987 
   5988 	    case IPP_TAG_RANGE :
   5989 	        for (i = 0, value = attr->values;
   5990 		     i < attr->num_values;
   5991 		     i ++, value ++)
   5992 		{
   5993                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 13)
   5994 		  {
   5995                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   5996 	            {
   5997 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   5998 		                 "attribute...");
   5999 		      _cupsBufferRelease((char *)buffer);
   6000 	              return (IPP_STATE_ERROR);
   6001 	            }
   6002 
   6003 		    bufptr = buffer;
   6004 		  }
   6005 
   6006 		  if (i)
   6007 		  {
   6008 		   /*
   6009 		    * Arrays and sets are done by sending additional
   6010 		    * values with a zero-length name...
   6011 		    */
   6012 
   6013                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   6014 		    *bufptr++ = 0;
   6015 		    *bufptr++ = 0;
   6016 		  }
   6017 
   6018                  /*
   6019 		  * Range values consist of a 2-byte length,
   6020 		  * 4-byte lower value, and 4-byte upper value.
   6021 		  *
   6022 		  * Put the 2-byte length and range value data
   6023 		  * into the buffer.
   6024 		  */
   6025 
   6026 	          *bufptr++ = 0;
   6027 		  *bufptr++ = 8;
   6028 		  *bufptr++ = (ipp_uchar_t)(value->range.lower >> 24);
   6029 		  *bufptr++ = (ipp_uchar_t)(value->range.lower >> 16);
   6030 		  *bufptr++ = (ipp_uchar_t)(value->range.lower >> 8);
   6031 		  *bufptr++ = (ipp_uchar_t)value->range.lower;
   6032 		  *bufptr++ = (ipp_uchar_t)(value->range.upper >> 24);
   6033 		  *bufptr++ = (ipp_uchar_t)(value->range.upper >> 16);
   6034 		  *bufptr++ = (ipp_uchar_t)(value->range.upper >> 8);
   6035 		  *bufptr++ = (ipp_uchar_t)value->range.upper;
   6036 		}
   6037 		break;
   6038 
   6039 	    case IPP_TAG_TEXTLANG :
   6040 	    case IPP_TAG_NAMELANG :
   6041 	        for (i = 0, value = attr->values;
   6042 		     i < attr->num_values;
   6043 		     i ++, value ++)
   6044 		{
   6045 		  if (i)
   6046 		  {
   6047 		   /*
   6048 		    * Arrays and sets are done by sending additional
   6049 		    * values with a zero-length name...
   6050 		    */
   6051 
   6052                     if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
   6053 		    {
   6054                       if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6055 	              {
   6056 	        	DEBUG_puts("1ippWriteIO: Could not write IPP "
   6057 		                   "attribute...");
   6058 			_cupsBufferRelease((char *)buffer);
   6059 	        	return (IPP_STATE_ERROR);
   6060 	              }
   6061 
   6062 		      bufptr = buffer;
   6063 		    }
   6064 
   6065                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   6066 		    *bufptr++ = 0;
   6067 		    *bufptr++ = 0;
   6068 		  }
   6069 
   6070                  /*
   6071 		  * textWithLanguage and nameWithLanguage values consist
   6072 		  * of a 2-byte length for both strings and their
   6073 		  * individual lengths, a 2-byte length for the
   6074 		  * character string, the character string without the
   6075 		  * trailing nul, a 2-byte length for the character
   6076 		  * set string, and the character set string without
   6077 		  * the trailing nul.
   6078 		  */
   6079 
   6080                   n = 4;
   6081 
   6082 		  if (value->string.language != NULL)
   6083                     n += (int)strlen(value->string.language);
   6084 
   6085 		  if (value->string.text != NULL)
   6086                     n += (int)strlen(value->string.text);
   6087 
   6088                   if (n > (IPP_BUF_SIZE - 2))
   6089 		  {
   6090 		    DEBUG_printf(("1ippWriteIO: text/nameWithLanguage value "
   6091 		                  "too long (%d)", n));
   6092 		    _cupsBufferRelease((char *)buffer);
   6093 		    return (IPP_STATE_ERROR);
   6094                   }
   6095 
   6096                   if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
   6097 		  {
   6098                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6099 	            {
   6100 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   6101 		                 "attribute...");
   6102 		      _cupsBufferRelease((char *)buffer);
   6103 	              return (IPP_STATE_ERROR);
   6104 	            }
   6105 
   6106 		    bufptr = buffer;
   6107 		  }
   6108 
   6109                  /* Length of entire value */
   6110 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
   6111 		  *bufptr++ = (ipp_uchar_t)n;
   6112 
   6113                  /* Length of language */
   6114 		  if (value->string.language != NULL)
   6115 		    n = (int)strlen(value->string.language);
   6116 		  else
   6117 		    n = 0;
   6118 
   6119 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
   6120 		  *bufptr++ = (ipp_uchar_t)n;
   6121 
   6122                  /* Language */
   6123 		  if (n > 0)
   6124 		  {
   6125 		    memcpy(bufptr, value->string.language, (size_t)n);
   6126 		    bufptr += n;
   6127 		  }
   6128 
   6129                  /* Length of text */
   6130                   if (value->string.text != NULL)
   6131 		    n = (int)strlen(value->string.text);
   6132 		  else
   6133 		    n = 0;
   6134 
   6135 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
   6136 		  *bufptr++ = (ipp_uchar_t)n;
   6137 
   6138                  /* Text */
   6139 		  if (n > 0)
   6140 		  {
   6141 		    memcpy(bufptr, value->string.text, (size_t)n);
   6142 		    bufptr += n;
   6143 		  }
   6144 		}
   6145 		break;
   6146 
   6147             case IPP_TAG_BEGIN_COLLECTION :
   6148 	        for (i = 0, value = attr->values;
   6149 		     i < attr->num_values;
   6150 		     i ++, value ++)
   6151 		{
   6152 		 /*
   6153 		  * Collections are written with the begin-collection
   6154 		  * tag first with a value of 0 length, followed by the
   6155 		  * attributes in the collection, then the end-collection
   6156 		  * value...
   6157 		  */
   6158 
   6159                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 5)
   6160 		  {
   6161                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6162 	            {
   6163 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   6164 		                 "attribute...");
   6165 		      _cupsBufferRelease((char *)buffer);
   6166 	              return (IPP_STATE_ERROR);
   6167 	            }
   6168 
   6169 		    bufptr = buffer;
   6170 		  }
   6171 
   6172 		  if (i)
   6173 		  {
   6174 		   /*
   6175 		    * Arrays and sets are done by sending additional
   6176 		    * values with a zero-length name...
   6177 		    */
   6178 
   6179                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   6180 		    *bufptr++ = 0;
   6181 		    *bufptr++ = 0;
   6182 		  }
   6183 
   6184                  /*
   6185 		  * Write a data length of 0 and flush the buffer...
   6186 		  */
   6187 
   6188 	          *bufptr++ = 0;
   6189 		  *bufptr++ = 0;
   6190 
   6191                   if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6192 	          {
   6193 	            DEBUG_puts("1ippWriteIO: Could not write IPP "
   6194 		               "attribute...");
   6195 		    _cupsBufferRelease((char *)buffer);
   6196 	            return (IPP_STATE_ERROR);
   6197 	          }
   6198 
   6199 		  bufptr = buffer;
   6200 
   6201                  /*
   6202 		  * Then write the collection attribute...
   6203 		  */
   6204 
   6205                   value->collection->state = IPP_STATE_IDLE;
   6206 
   6207 		  if (ippWriteIO(dst, cb, 1, ipp,
   6208 		                 value->collection) == IPP_STATE_ERROR)
   6209 		  {
   6210 		    DEBUG_puts("1ippWriteIO: Unable to write collection value");
   6211 		    _cupsBufferRelease((char *)buffer);
   6212 		    return (IPP_STATE_ERROR);
   6213 		  }
   6214 		}
   6215 		break;
   6216 
   6217             default :
   6218 	        for (i = 0, value = attr->values;
   6219 		     i < attr->num_values;
   6220 		     i ++, value ++)
   6221 		{
   6222 		  if (i)
   6223 		  {
   6224 		   /*
   6225 		    * Arrays and sets are done by sending additional
   6226 		    * values with a zero-length name...
   6227 		    */
   6228 
   6229                     if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
   6230 		    {
   6231                       if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6232 	              {
   6233 	        	DEBUG_puts("1ippWriteIO: Could not write IPP "
   6234 		                   "attribute...");
   6235 			_cupsBufferRelease((char *)buffer);
   6236 	        	return (IPP_STATE_ERROR);
   6237 	              }
   6238 
   6239 		      bufptr = buffer;
   6240 		    }
   6241 
   6242                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
   6243 		    *bufptr++ = 0;
   6244 		    *bufptr++ = 0;
   6245 		  }
   6246 
   6247                  /*
   6248 		  * An unknown value might some new value that a
   6249 		  * vendor has come up with. It consists of a
   6250 		  * 2-byte length and the bytes in the unknown
   6251 		  * value buffer.
   6252 		  */
   6253 
   6254                   n = value->unknown.length;
   6255 
   6256                   if (n > (IPP_BUF_SIZE - 2))
   6257 		  {
   6258 		    DEBUG_printf(("1ippWriteIO: Data length too long (%d)",
   6259 		                  n));
   6260 		    _cupsBufferRelease((char *)buffer);
   6261 		    return (IPP_STATE_ERROR);
   6262 		  }
   6263 
   6264                   if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
   6265 		  {
   6266                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6267 	            {
   6268 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
   6269 		                 "attribute...");
   6270 		      _cupsBufferRelease((char *)buffer);
   6271 	              return (IPP_STATE_ERROR);
   6272 	            }
   6273 
   6274 		    bufptr = buffer;
   6275 		  }
   6276 
   6277                  /* Length of unknown value */
   6278 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
   6279 		  *bufptr++ = (ipp_uchar_t)n;
   6280 
   6281                  /* Value */
   6282 		  if (n > 0)
   6283 		  {
   6284 		    memcpy(bufptr, value->unknown.data, (size_t)n);
   6285 		    bufptr += n;
   6286 		  }
   6287 		}
   6288 		break;
   6289 	  }
   6290 
   6291          /*
   6292 	  * Write the data out...
   6293 	  */
   6294 
   6295 	  if (bufptr > buffer)
   6296 	  {
   6297 	    if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
   6298 	    {
   6299 	      DEBUG_puts("1ippWriteIO: Could not write IPP attribute...");
   6300 	      _cupsBufferRelease((char *)buffer);
   6301 	      return (IPP_STATE_ERROR);
   6302 	    }
   6303 
   6304 	    DEBUG_printf(("2ippWriteIO: wrote %d bytes",
   6305 			  (int)(bufptr - buffer)));
   6306 	  }
   6307 
   6308 	 /*
   6309           * If blocking is disabled and we aren't at the end of the attribute
   6310           * list, stop here...
   6311 	  */
   6312 
   6313           if (!blocking && ipp->current)
   6314 	    break;
   6315 	}
   6316 
   6317 	if (ipp->current == NULL)
   6318 	{
   6319          /*
   6320 	  * Done with all of the attributes; add the end-of-attributes
   6321 	  * tag or end-collection attribute...
   6322 	  */
   6323 
   6324           if (parent == NULL)
   6325 	  {
   6326             buffer[0] = IPP_TAG_END;
   6327 	    n         = 1;
   6328 	  }
   6329 	  else
   6330 	  {
   6331             buffer[0] = IPP_TAG_END_COLLECTION;
   6332 	    buffer[1] = 0; /* empty name */
   6333 	    buffer[2] = 0;
   6334 	    buffer[3] = 0; /* empty value */
   6335 	    buffer[4] = 0;
   6336 	    n         = 5;
   6337 	  }
   6338 
   6339 	  if ((*cb)(dst, buffer, (size_t)n) < 0)
   6340 	  {
   6341 	    DEBUG_puts("1ippWriteIO: Could not write IPP end-tag...");
   6342 	    _cupsBufferRelease((char *)buffer);
   6343 	    return (IPP_STATE_ERROR);
   6344 	  }
   6345 
   6346 	  ipp->state = IPP_STATE_DATA;
   6347 	}
   6348         break;
   6349 
   6350     case IPP_STATE_DATA :
   6351         break;
   6352 
   6353     default :
   6354         break; /* anti-compiler-warning-code */
   6355   }
   6356 
   6357   _cupsBufferRelease((char *)buffer);
   6358 
   6359   return (ipp->state);
   6360 }
   6361 
   6362 
   6363 /*
   6364  * 'ipp_add_attr()' - Add a new attribute to the message.
   6365  */
   6366 
   6367 static ipp_attribute_t *		/* O - New attribute */
   6368 ipp_add_attr(ipp_t      *ipp,		/* I - IPP message */
   6369              const char *name,		/* I - Attribute name or NULL */
   6370              ipp_tag_t  group_tag,	/* I - Group tag or IPP_TAG_ZERO */
   6371              ipp_tag_t  value_tag,	/* I - Value tag or IPP_TAG_ZERO */
   6372              int        num_values)	/* I - Number of values */
   6373 {
   6374   int			alloc_values;	/* Number of values to allocate */
   6375   ipp_attribute_t	*attr;		/* New attribute */
   6376 
   6377 
   6378   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));
   6379 
   6380  /*
   6381   * Range check input...
   6382   */
   6383 
   6384   if (!ipp || num_values < 0)
   6385     return (NULL);
   6386 
   6387  /*
   6388   * Allocate memory, rounding the allocation up as needed...
   6389   */
   6390 
   6391   if (num_values <= 1)
   6392     alloc_values = 1;
   6393   else
   6394     alloc_values = (num_values + IPP_MAX_VALUES - 1) & ~(IPP_MAX_VALUES - 1);
   6395 
   6396   attr = calloc(sizeof(ipp_attribute_t) +
   6397                 (size_t)(alloc_values - 1) * sizeof(_ipp_value_t), 1);
   6398 
   6399   if (attr)
   6400   {
   6401    /*
   6402     * Initialize attribute...
   6403     */
   6404 
   6405     if (name)
   6406       attr->name = _cupsStrAlloc(name);
   6407 
   6408     attr->group_tag  = group_tag;
   6409     attr->value_tag  = value_tag;
   6410     attr->num_values = num_values;
   6411 
   6412    /*
   6413     * Add it to the end of the linked list...
   6414     */
   6415 
   6416     if (ipp->last)
   6417       ipp->last->next = attr;
   6418     else
   6419       ipp->attrs = attr;
   6420 
   6421     ipp->prev = ipp->last;
   6422     ipp->last = ipp->current = attr;
   6423   }
   6424 
   6425   DEBUG_printf(("5ipp_add_attr: Returning %p", (void *)attr));
   6426 
   6427   return (attr);
   6428 }
   6429 
   6430 
   6431 /*
   6432  * 'ipp_free_values()' - Free attribute values.
   6433  */
   6434 
   6435 static void
   6436 ipp_free_values(ipp_attribute_t *attr,	/* I - Attribute to free values from */
   6437                 int             element,/* I - First value to free */
   6438                 int             count)	/* I - Number of values to free */
   6439 {
   6440   int		i;			/* Looping var */
   6441   _ipp_value_t	*value;			/* Current value */
   6442 
   6443 
   6444   DEBUG_printf(("4ipp_free_values(attr=%p, element=%d, count=%d)", (void *)attr, element, count));
   6445 
   6446   if (!(attr->value_tag & IPP_TAG_CUPS_CONST))
   6447   {
   6448    /*
   6449     * Free values as needed...
   6450     */
   6451 
   6452     switch (attr->value_tag)
   6453     {
   6454       case IPP_TAG_TEXTLANG :
   6455       case IPP_TAG_NAMELANG :
   6456 	  if (element == 0 && count == attr->num_values &&
   6457 	      attr->values[0].string.language)
   6458 	  {
   6459 	    _cupsStrFree(attr->values[0].string.language);
   6460 	    attr->values[0].string.language = NULL;
   6461 	  }
   6462 	  /* Fall through to other string values */
   6463 
   6464       case IPP_TAG_TEXT :
   6465       case IPP_TAG_NAME :
   6466       case IPP_TAG_RESERVED_STRING :
   6467       case IPP_TAG_KEYWORD :
   6468       case IPP_TAG_URI :
   6469       case IPP_TAG_URISCHEME :
   6470       case IPP_TAG_CHARSET :
   6471       case IPP_TAG_LANGUAGE :
   6472       case IPP_TAG_MIMETYPE :
   6473 	  for (i = count, value = attr->values + element;
   6474 	       i > 0;
   6475 	       i --, value ++)
   6476 	  {
   6477 	    _cupsStrFree(value->string.text);
   6478 	    value->string.text = NULL;
   6479 	  }
   6480 	  break;
   6481 
   6482       case IPP_TAG_DEFAULT :
   6483       case IPP_TAG_UNKNOWN :
   6484       case IPP_TAG_NOVALUE :
   6485       case IPP_TAG_NOTSETTABLE :
   6486       case IPP_TAG_DELETEATTR :
   6487       case IPP_TAG_ADMINDEFINE :
   6488       case IPP_TAG_INTEGER :
   6489       case IPP_TAG_ENUM :
   6490       case IPP_TAG_BOOLEAN :
   6491       case IPP_TAG_DATE :
   6492       case IPP_TAG_RESOLUTION :
   6493       case IPP_TAG_RANGE :
   6494 	  break;
   6495 
   6496       case IPP_TAG_BEGIN_COLLECTION :
   6497 	  for (i = count, value = attr->values + element;
   6498 	       i > 0;
   6499 	       i --, value ++)
   6500 	  {
   6501 	    ippDelete(value->collection);
   6502 	    value->collection = NULL;
   6503 	  }
   6504 	  break;
   6505 
   6506       case IPP_TAG_STRING :
   6507       default :
   6508 	  for (i = count, value = attr->values + element;
   6509 	       i > 0;
   6510 	       i --, value ++)
   6511 	  {
   6512 	    if (value->unknown.data)
   6513 	    {
   6514 	      free(value->unknown.data);
   6515 	      value->unknown.data = NULL;
   6516 	    }
   6517 	  }
   6518 	  break;
   6519     }
   6520   }
   6521 
   6522  /*
   6523   * If we are not freeing values from the end, move the remaining values up...
   6524   */
   6525 
   6526   if ((element + count) < attr->num_values)
   6527     memmove(attr->values + element, attr->values + element + count,
   6528             (size_t)(attr->num_values - count - element) * sizeof(_ipp_value_t));
   6529 
   6530   attr->num_values -= count;
   6531 }
   6532 
   6533 
   6534 /*
   6535  * 'ipp_get_code()' - Convert a C locale/charset name into an IPP language/charset code.
   6536  *
   6537  * This typically converts strings of the form "ll_CC", "ll-REGION", and "CHARSET_NUMBER"
   6538  * to "ll-cc", "ll-region", and "charset-number", respectively.
   6539  */
   6540 
   6541 static char *				/* O - Language code string */
   6542 ipp_get_code(const char *value,		/* I - Locale/charset string */
   6543              char       *buffer,	/* I - String buffer */
   6544              size_t     bufsize)	/* I - Size of string buffer */
   6545 {
   6546   char	*bufptr,			/* Pointer into buffer */
   6547 	*bufend;			/* End of buffer */
   6548 
   6549 
   6550  /*
   6551   * Convert values to lowercase and change _ to - as needed...
   6552   */
   6553 
   6554   for (bufptr = buffer, bufend = buffer + bufsize - 1;
   6555        *value && bufptr < bufend;
   6556        value ++)
   6557     if (*value == '_')
   6558       *bufptr++ = '-';
   6559     else
   6560       *bufptr++ = (char)_cups_tolower(*value);
   6561 
   6562   *bufptr = '\0';
   6563 
   6564  /*
   6565   * Return the converted string...
   6566   */
   6567 
   6568   return (buffer);
   6569 }
   6570 
   6571 
   6572 /*
   6573  * 'ipp_lang_code()' - Convert a C locale name into an IPP language code.
   6574  *
   6575  * This typically converts strings of the form "ll_CC" and "ll-REGION" to "ll-cc" and
   6576  * "ll-region", respectively.  It also converts the "C" (POSIX) locale to "en".
   6577  */
   6578 
   6579 static char *				/* O - Language code string */
   6580 ipp_lang_code(const char *locale,	/* I - Locale string */
   6581               char       *buffer,	/* I - String buffer */
   6582               size_t     bufsize)	/* I - Size of string buffer */
   6583 {
   6584  /*
   6585   * Map POSIX ("C") locale to generic English, otherwise convert the locale string as-is.
   6586   */
   6587 
   6588   if (!_cups_strcasecmp(locale, "c"))
   6589   {
   6590     strlcpy(buffer, "en", bufsize);
   6591     return (buffer);
   6592   }
   6593   else
   6594     return (ipp_get_code(locale, buffer, bufsize));
   6595 }
   6596 
   6597 
   6598 /*
   6599  * 'ipp_length()' - Compute the length of an IPP message or collection value.
   6600  */
   6601 
   6602 static size_t				/* O - Size of IPP message */
   6603 ipp_length(ipp_t *ipp,			/* I - IPP message or collection */
   6604            int   collection)		/* I - 1 if a collection, 0 otherwise */
   6605 {
   6606   int			i;		/* Looping var */
   6607   size_t		bytes;		/* Number of bytes */
   6608   ipp_attribute_t	*attr;		/* Current attribute */
   6609   ipp_tag_t		group;		/* Current group */
   6610   _ipp_value_t		*value;		/* Current value */
   6611 
   6612 
   6613   DEBUG_printf(("3ipp_length(ipp=%p, collection=%d)", (void *)ipp, collection));
   6614 
   6615   if (!ipp)
   6616   {
   6617     DEBUG_puts("4ipp_length: Returning 0 bytes");
   6618     return (0);
   6619   }
   6620 
   6621  /*
   6622   * Start with 8 bytes for the IPP message header...
   6623   */
   6624 
   6625   bytes = collection ? 0 : 8;
   6626 
   6627  /*
   6628   * Then add the lengths of each attribute...
   6629   */
   6630 
   6631   group = IPP_TAG_ZERO;
   6632 
   6633   for (attr = ipp->attrs; attr != NULL; attr = attr->next)
   6634   {
   6635     if (attr->group_tag != group && !collection)
   6636     {
   6637       group = attr->group_tag;
   6638       if (group == IPP_TAG_ZERO)
   6639 	continue;
   6640 
   6641       bytes ++;	/* Group tag */
   6642     }
   6643 
   6644     if (!attr->name)
   6645       continue;
   6646 
   6647     DEBUG_printf(("5ipp_length: attr->name=\"%s\", attr->num_values=%d, "
   6648                   "bytes=" CUPS_LLFMT, attr->name, attr->num_values, CUPS_LLCAST bytes));
   6649 
   6650     if ((attr->value_tag & ~IPP_TAG_CUPS_CONST) < IPP_TAG_EXTENSION)
   6651       bytes += (size_t)attr->num_values;/* Value tag for each value */
   6652     else
   6653       bytes += (size_t)(5 * attr->num_values);
   6654 					/* Value tag for each value */
   6655     bytes += (size_t)(2 * attr->num_values);
   6656 					/* Name lengths */
   6657     bytes += strlen(attr->name);	/* Name */
   6658     bytes += (size_t)(2 * attr->num_values);
   6659 					/* Value lengths */
   6660 
   6661     if (collection)
   6662       bytes += 5;			/* Add membername overhead */
   6663 
   6664     switch (attr->value_tag & ~IPP_TAG_CUPS_CONST)
   6665     {
   6666       case IPP_TAG_UNSUPPORTED_VALUE :
   6667       case IPP_TAG_DEFAULT :
   6668       case IPP_TAG_UNKNOWN :
   6669       case IPP_TAG_NOVALUE :
   6670       case IPP_TAG_NOTSETTABLE :
   6671       case IPP_TAG_DELETEATTR :
   6672       case IPP_TAG_ADMINDEFINE :
   6673           break;
   6674 
   6675       case IPP_TAG_INTEGER :
   6676       case IPP_TAG_ENUM :
   6677           bytes += (size_t)(4 * attr->num_values);
   6678 	  break;
   6679 
   6680       case IPP_TAG_BOOLEAN :
   6681           bytes += (size_t)attr->num_values;
   6682 	  break;
   6683 
   6684       case IPP_TAG_TEXT :
   6685       case IPP_TAG_NAME :
   6686       case IPP_TAG_KEYWORD :
   6687       case IPP_TAG_URI :
   6688       case IPP_TAG_URISCHEME :
   6689       case IPP_TAG_CHARSET :
   6690       case IPP_TAG_LANGUAGE :
   6691       case IPP_TAG_MIMETYPE :
   6692 	  for (i = 0, value = attr->values;
   6693 	       i < attr->num_values;
   6694 	       i ++, value ++)
   6695 	    if (value->string.text)
   6696 	      bytes += strlen(value->string.text);
   6697 	  break;
   6698 
   6699       case IPP_TAG_DATE :
   6700           bytes += (size_t)(11 * attr->num_values);
   6701 	  break;
   6702 
   6703       case IPP_TAG_RESOLUTION :
   6704           bytes += (size_t)(9 * attr->num_values);
   6705 	  break;
   6706 
   6707       case IPP_TAG_RANGE :
   6708           bytes += (size_t)(8 * attr->num_values);
   6709 	  break;
   6710 
   6711       case IPP_TAG_TEXTLANG :
   6712       case IPP_TAG_NAMELANG :
   6713           bytes += (size_t)(4 * attr->num_values);
   6714 					/* Charset + text length */
   6715 
   6716 	  for (i = 0, value = attr->values;
   6717 	       i < attr->num_values;
   6718 	       i ++, value ++)
   6719 	  {
   6720 	    if (value->string.language)
   6721 	      bytes += strlen(value->string.language);
   6722 
   6723 	    if (value->string.text)
   6724 	      bytes += strlen(value->string.text);
   6725 	  }
   6726 	  break;
   6727 
   6728       case IPP_TAG_BEGIN_COLLECTION :
   6729 	  for (i = 0, value = attr->values;
   6730 	       i < attr->num_values;
   6731 	       i ++, value ++)
   6732             bytes += ipp_length(value->collection, 1);
   6733 	  break;
   6734 
   6735       default :
   6736 	  for (i = 0, value = attr->values;
   6737 	       i < attr->num_values;
   6738 	       i ++, value ++)
   6739             bytes += (size_t)value->unknown.length;
   6740 	  break;
   6741     }
   6742   }
   6743 
   6744  /*
   6745   * Finally, add 1 byte for the "end of attributes" tag or 5 bytes
   6746   * for the "end of collection" tag and return...
   6747   */
   6748 
   6749   if (collection)
   6750     bytes += 5;
   6751   else
   6752     bytes ++;
   6753 
   6754   DEBUG_printf(("4ipp_length: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST bytes));
   6755 
   6756   return (bytes);
   6757 }
   6758 
   6759 
   6760 /*
   6761  * 'ipp_read_http()' - Semi-blocking read on a HTTP connection...
   6762  */
   6763 
   6764 static ssize_t				/* O - Number of bytes read */
   6765 ipp_read_http(http_t      *http,	/* I - Client connection */
   6766               ipp_uchar_t *buffer,	/* O - Buffer for data */
   6767 	      size_t      length)	/* I - Total length */
   6768 {
   6769   ssize_t	tbytes,			/* Total bytes read */
   6770 		bytes;			/* Bytes read this pass */
   6771 
   6772 
   6773   DEBUG_printf(("7ipp_read_http(http=%p, buffer=%p, length=%d)", (void *)http, (void *)buffer, (int)length));
   6774 
   6775  /*
   6776   * Loop until all bytes are read...
   6777   */
   6778 
   6779   for (tbytes = 0, bytes = 0;
   6780        tbytes < (int)length;
   6781        tbytes += bytes, buffer += bytes)
   6782   {
   6783     DEBUG_printf(("9ipp_read_http: tbytes=" CUPS_LLFMT ", http->state=%d", CUPS_LLCAST tbytes, http->state));
   6784 
   6785     if (http->state == HTTP_STATE_WAITING)
   6786       break;
   6787 
   6788     if (http->used == 0 && !http->blocking)
   6789     {
   6790      /*
   6791       * Wait up to 10 seconds for more data on non-blocking sockets...
   6792       */
   6793 
   6794       if (!httpWait(http, 10000))
   6795       {
   6796        /*
   6797 	* Signal no data...
   6798 	*/
   6799 
   6800 	bytes = -1;
   6801 	break;
   6802       }
   6803     }
   6804     else if (http->used == 0 && http->timeout_value > 0)
   6805     {
   6806      /*
   6807       * Wait up to timeout seconds for more data on blocking sockets...
   6808       */
   6809 
   6810       if (!httpWait(http, (int)(1000 * http->timeout_value)))
   6811       {
   6812        /*
   6813 	* Signal no data...
   6814 	*/
   6815 
   6816 	bytes = -1;
   6817 	break;
   6818       }
   6819     }
   6820 
   6821     if ((bytes = httpRead2(http, (char *)buffer, length - (size_t)tbytes)) < 0)
   6822     {
   6823 #ifdef WIN32
   6824       break;
   6825 #else
   6826       if (errno != EAGAIN && errno != EINTR)
   6827 	break;
   6828 
   6829       bytes = 0;
   6830 #endif /* WIN32 */
   6831     }
   6832     else if (bytes == 0)
   6833       break;
   6834   }
   6835 
   6836  /*
   6837   * Return the number of bytes read...
   6838   */
   6839 
   6840   if (tbytes == 0 && bytes < 0)
   6841     tbytes = -1;
   6842 
   6843   DEBUG_printf(("8ipp_read_http: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST tbytes));
   6844 
   6845   return (tbytes);
   6846 }
   6847 
   6848 
   6849 /*
   6850  * 'ipp_read_file()' - Read IPP data from a file.
   6851  */
   6852 
   6853 static ssize_t				/* O - Number of bytes read */
   6854 ipp_read_file(int         *fd,		/* I - File descriptor */
   6855               ipp_uchar_t *buffer,	/* O - Read buffer */
   6856 	      size_t      length)	/* I - Number of bytes to read */
   6857 {
   6858 #ifdef WIN32
   6859   return ((ssize_t)read(*fd, buffer, (unsigned)length));
   6860 #else
   6861   return (read(*fd, buffer, length));
   6862 #endif /* WIN32 */
   6863 }
   6864 
   6865 
   6866 /*
   6867  * 'ipp_set_error()' - Set a formatted, localized error string.
   6868  */
   6869 
   6870 static void
   6871 ipp_set_error(ipp_status_t status,	/* I - Status code */
   6872               const char   *format,	/* I - Printf-style error string */
   6873 	      ...)			/* I - Additional arguments as needed */
   6874 {
   6875   va_list	ap;			/* Pointer to additional args */
   6876   char		buffer[2048];		/* Message buffer */
   6877   cups_lang_t	*lang = cupsLangDefault();
   6878 					/* Current language */
   6879 
   6880 
   6881   va_start(ap, format);
   6882   vsnprintf(buffer, sizeof(buffer), _cupsLangString(lang, format), ap);
   6883   va_end(ap);
   6884 
   6885   _cupsSetError(status, buffer, 0);
   6886 }
   6887 
   6888 
   6889 /*
   6890  * 'ipp_set_value()' - Get the value element from an attribute, expanding it as
   6891  *                     needed.
   6892  */
   6893 
   6894 static _ipp_value_t *			/* O  - IPP value element or NULL on error */
   6895 ipp_set_value(ipp_t           *ipp,	/* IO - IPP message */
   6896               ipp_attribute_t **attr,	/* IO - IPP attribute */
   6897               int             element)	/* I  - Value number (0-based) */
   6898 {
   6899   ipp_attribute_t	*temp,		/* New attribute pointer */
   6900 			*current,	/* Current attribute in list */
   6901 			*prev;		/* Previous attribute in list */
   6902   int			alloc_values;	/* Allocated values */
   6903 
   6904 
   6905  /*
   6906   * If we are setting an existing value element, return it...
   6907   */
   6908 
   6909   temp = *attr;
   6910 
   6911   if (temp->num_values <= 1)
   6912     alloc_values = 1;
   6913   else
   6914     alloc_values = (temp->num_values + IPP_MAX_VALUES - 1) &
   6915                    ~(IPP_MAX_VALUES - 1);
   6916 
   6917   if (element < alloc_values)
   6918   {
   6919     if (element >= temp->num_values)
   6920       temp->num_values = element + 1;
   6921 
   6922     return (temp->values + element);
   6923   }
   6924 
   6925  /*
   6926   * Otherwise re-allocate the attribute - we allocate in groups of IPP_MAX_VALUE
   6927   * values when num_values > 1.
   6928   */
   6929 
   6930   if (alloc_values < IPP_MAX_VALUES)
   6931     alloc_values = IPP_MAX_VALUES;
   6932   else
   6933     alloc_values += IPP_MAX_VALUES;
   6934 
   6935   DEBUG_printf(("4ipp_set_value: Reallocating for up to %d values.",
   6936                 alloc_values));
   6937 
   6938  /*
   6939   * Reallocate memory...
   6940   */
   6941 
   6942   if ((temp = realloc(temp, sizeof(ipp_attribute_t) + (size_t)(alloc_values - 1) * sizeof(_ipp_value_t))) == NULL)
   6943   {
   6944     _cupsSetHTTPError(HTTP_STATUS_ERROR);
   6945     DEBUG_puts("4ipp_set_value: Unable to resize attribute.");
   6946     return (NULL);
   6947   }
   6948 
   6949  /*
   6950   * Zero the new memory...
   6951   */
   6952 
   6953   memset(temp->values + temp->num_values, 0, (size_t)(alloc_values - temp->num_values) * sizeof(_ipp_value_t));
   6954 
   6955   if (temp != *attr)
   6956   {
   6957    /*
   6958     * Reset pointers in the list...
   6959     */
   6960 
   6961     if (ipp->current == *attr && ipp->prev)
   6962     {
   6963      /*
   6964       * Use current "previous" pointer...
   6965       */
   6966 
   6967       prev = ipp->prev;
   6968     }
   6969     else
   6970     {
   6971      /*
   6972       * Find this attribute in the linked list...
   6973       */
   6974 
   6975       for (prev = NULL, current = ipp->attrs;
   6976 	   current && current != *attr;
   6977 	   prev = current, current = current->next);
   6978 
   6979       if (!current)
   6980       {
   6981        /*
   6982 	* This is a serious error!
   6983 	*/
   6984 
   6985 	*attr = temp;
   6986 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL,
   6987 	              _("IPP attribute is not a member of the message."), 1);
   6988 	DEBUG_puts("4ipp_set_value: Unable to find attribute in message.");
   6989 	return (NULL);
   6990       }
   6991     }
   6992 
   6993     if (prev)
   6994       prev->next = temp;
   6995     else
   6996       ipp->attrs = temp;
   6997 
   6998     ipp->current = temp;
   6999     ipp->prev    = prev;
   7000 
   7001     if (ipp->last == *attr)
   7002       ipp->last = temp;
   7003 
   7004     *attr = temp;
   7005   }
   7006 
   7007  /*
   7008   * Return the value element...
   7009   */
   7010 
   7011   if (element >= temp->num_values)
   7012     temp->num_values = element + 1;
   7013 
   7014   return (temp->values + element);
   7015 }
   7016 
   7017 
   7018 /*
   7019  * 'ipp_write_file()' - Write IPP data to a file.
   7020  */
   7021 
   7022 static ssize_t				/* O - Number of bytes written */
   7023 ipp_write_file(int         *fd,		/* I - File descriptor */
   7024                ipp_uchar_t *buffer,	/* I - Data to write */
   7025                size_t      length)	/* I - Number of bytes to write */
   7026 {
   7027 #ifdef WIN32
   7028   return ((ssize_t)write(*fd, buffer, (unsigned)length));
   7029 #else
   7030   return (write(*fd, buffer, length));
   7031 #endif /* WIN32 */
   7032 }
   7033