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