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