2 * Internet Printing Protocol functions for CUPS.
4 * Copyright 2007-2017 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
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/".
13 * This file is subject to the Apple OS-Developed Software exception.
17 * Include necessary headers...
20 #include "cups-private.h"
31 static ipp_attribute_t *ipp_add_attr(ipp_t *ipp, const char *name,
32 ipp_tag_t group_tag, ipp_tag_t value_tag,
34 static void ipp_free_values(ipp_attribute_t *attr, int element,
36 static char *ipp_get_code(const char *locale, char *buffer,
38 __attribute__((nonnull(1,2)));
39 static char *ipp_lang_code(const char *locale, char *buffer,
41 __attribute__((nonnull(1,2)));
42 static size_t ipp_length(ipp_t *ipp, int collection);
43 static ssize_t ipp_read_http(http_t *http, ipp_uchar_t *buffer,
45 static ssize_t ipp_read_file(int *fd, ipp_uchar_t *buffer,
47 static void ipp_set_error(ipp_status_t status, const char *format,
49 static _ipp_value_t *ipp_set_value(ipp_t *ipp, ipp_attribute_t **attr,
51 static ssize_t ipp_write_file(int *fd, ipp_uchar_t *buffer,
56 * '_cupsBufferGet()' - Get a read/write buffer.
59 char * /* O - Buffer */
60 _cupsBufferGet(size_t size) /* I - Size required */
62 _cups_buffer_t *buffer; /* Current buffer */
63 _cups_globals_t *cg = _cupsGlobals();
67 for (buffer = cg->cups_buffers; buffer; buffer = buffer->next)
68 if (!buffer->used && buffer->size >= size)
73 if ((buffer = malloc(sizeof(_cups_buffer_t) + size - 1)) == NULL)
76 buffer->next = cg->cups_buffers;
78 cg->cups_buffers = buffer;
88 * '_cupsBufferRelease()' - Release a read/write buffer.
92 _cupsBufferRelease(char *b) /* I - Buffer to release */
94 _cups_buffer_t *buffer; /* Buffer */
98 * Mark this buffer as unused...
101 buffer = (_cups_buffer_t *)(b - offsetof(_cups_buffer_t, d));
107 * 'ippAddBoolean()' - Add a boolean attribute to an IPP message.
109 * The @code ipp@ parameter refers to an IPP message previously created using
110 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
119 ipp_attribute_t * /* O - New attribute */
120 ippAddBoolean(ipp_t *ipp, /* I - IPP message */
121 ipp_tag_t group, /* I - IPP group */
122 const char *name, /* I - Name of attribute */
123 char value) /* I - Value of attribute */
125 ipp_attribute_t *attr; /* New attribute */
128 DEBUG_printf(("ippAddBoolean(ipp=%p, group=%02x(%s), name=\"%s\", value=%d)", (void *)ipp, group, ippTagString(group), name, value));
131 * Range check input...
134 if (!ipp || !name || group < IPP_TAG_ZERO ||
135 group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
139 * Create the attribute...
142 if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BOOLEAN, 1)) == NULL)
145 attr->values[0].boolean = value;
152 * 'ippAddBooleans()' - Add an array of boolean values.
154 * The @code ipp@ parameter refers to an IPP message previously created using
155 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
164 ipp_attribute_t * /* O - New attribute */
165 ippAddBooleans(ipp_t *ipp, /* I - IPP message */
166 ipp_tag_t group, /* I - IPP group */
167 const char *name, /* I - Name of attribute */
168 int num_values, /* I - Number of values */
169 const char *values) /* I - Values */
171 int i; /* Looping var */
172 ipp_attribute_t *attr; /* New attribute */
173 _ipp_value_t *value; /* Current value */
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));
179 * Range check input...
182 if (!ipp || !name || group < IPP_TAG_ZERO ||
183 group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
188 * Create the attribute...
191 if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BOOLEAN, num_values)) == NULL)
196 for (i = num_values, value = attr->values;
199 value->boolean = *values++;
207 * 'ippAddCollection()' - Add a collection value.
209 * The @code ipp@ parameter refers to an IPP message previously created using
210 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
218 * @since CUPS 1.1.19/macOS 10.3@
221 ipp_attribute_t * /* O - New attribute */
222 ippAddCollection(ipp_t *ipp, /* I - IPP message */
223 ipp_tag_t group, /* I - IPP group */
224 const char *name, /* I - Name of attribute */
225 ipp_t *value) /* I - Value */
227 ipp_attribute_t *attr; /* New attribute */
230 DEBUG_printf(("ippAddCollection(ipp=%p, group=%02x(%s), name=\"%s\", value=%p)", (void *)ipp, group, ippTagString(group), name, (void *)value));
233 * Range check input...
236 if (!ipp || !name || group < IPP_TAG_ZERO ||
237 group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
241 * Create the attribute...
244 if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BEGIN_COLLECTION, 1)) == NULL)
247 attr->values[0].collection = value;
257 * 'ippAddCollections()' - Add an array of collection values.
259 * The @code ipp@ parameter refers to an IPP message previously created using
260 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
268 * @since CUPS 1.1.19/macOS 10.3@
271 ipp_attribute_t * /* O - New attribute */
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 */
279 int i; /* Looping var */
280 ipp_attribute_t *attr; /* New attribute */
281 _ipp_value_t *value; /* Current value */
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));
287 * Range check input...
290 if (!ipp || !name || group < IPP_TAG_ZERO ||
291 group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
296 * Create the attribute...
299 if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BEGIN_COLLECTION,
300 num_values)) == NULL)
305 for (i = num_values, value = attr->values;
309 value->collection = (ipp_t *)*values++;
310 value->collection->use ++;
319 * 'ippAddDate()' - Add a dateTime attribute to an IPP message.
321 * The @code ipp@ parameter refers to an IPP message previously created using
322 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
331 ipp_attribute_t * /* O - New attribute */
332 ippAddDate(ipp_t *ipp, /* I - IPP message */
333 ipp_tag_t group, /* I - IPP group */
334 const char *name, /* I - Name of attribute */
335 const ipp_uchar_t *value) /* I - Value */
337 ipp_attribute_t *attr; /* New attribute */
340 DEBUG_printf(("ippAddDate(ipp=%p, group=%02x(%s), name=\"%s\", value=%p)", (void *)ipp, group, ippTagString(group), name, (void *)value));
343 * Range check input...
346 if (!ipp || !name || !value || group < IPP_TAG_ZERO ||
347 group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
351 * Create the attribute...
354 if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_DATE, 1)) == NULL)
357 memcpy(attr->values[0].date, value, 11);
364 * 'ippAddInteger()' - Add a integer attribute to an IPP message.
366 * The @code ipp@ parameter refers to an IPP message previously created using
367 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
375 * Supported values include enum (@code IPP_TAG_ENUM@) and integer
376 * (@code IPP_TAG_INTEGER@).
379 ipp_attribute_t * /* O - New attribute */
380 ippAddInteger(ipp_t *ipp, /* I - IPP message */
381 ipp_tag_t group, /* I - IPP group */
382 ipp_tag_t value_tag, /* I - Type of attribute */
383 const char *name, /* I - Name of attribute */
384 int value) /* I - Value of attribute */
386 ipp_attribute_t *attr; /* New attribute */
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));
391 value_tag &= IPP_TAG_CUPS_MASK;
394 * Special-case for legacy usage: map out-of-band attributes to new ippAddOutOfBand
398 if (value_tag >= IPP_TAG_UNSUPPORTED_VALUE && value_tag <= IPP_TAG_ADMINDEFINE)
399 return (ippAddOutOfBand(ipp, group, value_tag, name));
402 * Range check input...
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))
411 if (!ipp || !name || group < IPP_TAG_ZERO ||
412 group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
417 * Create the attribute...
420 if ((attr = ipp_add_attr(ipp, name, group, value_tag, 1)) == NULL)
423 attr->values[0].integer = value;
430 * 'ippAddIntegers()' - Add an array of integer values.
432 * The @code ipp@ parameter refers to an IPP message previously created using
433 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
441 * Supported values include enum (@code IPP_TAG_ENUM@) and integer
442 * (@code IPP_TAG_INTEGER@).
445 ipp_attribute_t * /* O - New attribute */
446 ippAddIntegers(ipp_t *ipp, /* I - IPP message */
447 ipp_tag_t group, /* I - IPP group */
448 ipp_tag_t value_tag, /* I - Type of attribute */
449 const char *name, /* I - Name of attribute */
450 int num_values, /* I - Number of values */
451 const int *values) /* I - Values */
453 int i; /* Looping var */
454 ipp_attribute_t *attr; /* New attribute */
455 _ipp_value_t *value; /* Current value */
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));
460 value_tag &= IPP_TAG_CUPS_MASK;
463 * Range check input...
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) ||
473 if (!ipp || !name || group < IPP_TAG_ZERO ||
474 group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
480 * Create the attribute...
483 if ((attr = ipp_add_attr(ipp, name, group, value_tag, num_values)) == NULL)
488 for (i = num_values, value = attr->values;
491 value->integer = *values++;
499 * 'ippAddOctetString()' - Add an octetString value to an IPP message.
501 * The @code ipp@ parameter refers to an IPP message previously created using
502 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
510 * @since CUPS 1.2/macOS 10.5@
513 ipp_attribute_t * /* O - New attribute */
514 ippAddOctetString(ipp_t *ipp, /* I - IPP message */
515 ipp_tag_t group, /* I - IPP group */
516 const char *name, /* I - Name of attribute */
517 const void *data, /* I - octetString data */
518 int datalen) /* I - Length of data in bytes */
520 ipp_attribute_t *attr; /* New attribute */
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)
528 if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_STRING, 1)) == NULL)
532 * Initialize the attribute data...
535 attr->values[0].unknown.length = datalen;
539 if ((attr->values[0].unknown.data = malloc((size_t)datalen)) == NULL)
541 ippDeleteAttribute(ipp, attr);
545 memcpy(attr->values[0].unknown.data, data, (size_t)datalen);
549 * Return the new attribute...
557 * 'ippAddOutOfBand()' - Add an out-of-band value to an IPP message.
559 * The @code ipp@ parameter refers to an IPP message previously created using
560 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
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@).
574 * @since CUPS 1.6/macOS 10.8@
577 ipp_attribute_t * /* O - New attribute */
578 ippAddOutOfBand(ipp_t *ipp, /* I - IPP message */
579 ipp_tag_t group, /* I - IPP group */
580 ipp_tag_t value_tag, /* I - Type of attribute */
581 const char *name) /* I - Name of attribute */
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));
585 value_tag &= IPP_TAG_CUPS_MASK;
588 * Range check input...
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))
603 * Create the attribute...
606 return (ipp_add_attr(ipp, name, group, value_tag, 1));
611 * 'ippAddRange()' - Add a range of values to an IPP message.
613 * The @code ipp@ parameter refers to an IPP message previously created using
614 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
622 * The @code lower@ parameter must be less than or equal to the @code upper@ parameter.
625 ipp_attribute_t * /* O - New attribute */
626 ippAddRange(ipp_t *ipp, /* I - IPP message */
627 ipp_tag_t group, /* I - IPP group */
628 const char *name, /* I - Name of attribute */
629 int lower, /* I - Lower value */
630 int upper) /* I - Upper value */
632 ipp_attribute_t *attr; /* New attribute */
635 DEBUG_printf(("ippAddRange(ipp=%p, group=%02x(%s), name=\"%s\", lower=%d, upper=%d)", (void *)ipp, group, ippTagString(group), name, lower, upper));
638 * Range check input...
641 if (!ipp || !name || group < IPP_TAG_ZERO ||
642 group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
646 * Create the attribute...
649 if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RANGE, 1)) == NULL)
652 attr->values[0].range.lower = lower;
653 attr->values[0].range.upper = upper;
660 * 'ippAddRanges()' - Add ranges of values to an IPP message.
662 * The @code ipp@ parameter refers to an IPP message previously created using
663 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
672 ipp_attribute_t * /* O - New attribute */
673 ippAddRanges(ipp_t *ipp, /* I - IPP message */
674 ipp_tag_t group, /* I - IPP group */
675 const char *name, /* I - Name of attribute */
676 int num_values, /* I - Number of values */
677 const int *lower, /* I - Lower values */
678 const int *upper) /* I - Upper values */
680 int i; /* Looping var */
681 ipp_attribute_t *attr; /* New attribute */
682 _ipp_value_t *value; /* Current value */
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));
688 * Range check input...
691 if (!ipp || !name || group < IPP_TAG_ZERO ||
692 group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
697 * Create the attribute...
700 if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RANGE, num_values)) == NULL)
705 for (i = num_values, value = attr->values;
709 value->range.lower = *lower++;
710 value->range.upper = *upper++;
719 * 'ippAddResolution()' - Add a resolution value to an IPP message.
721 * The @code ipp@ parameter refers to an IPP message previously created using
722 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
731 ipp_attribute_t * /* O - New attribute */
732 ippAddResolution(ipp_t *ipp, /* I - IPP message */
733 ipp_tag_t group, /* I - IPP group */
734 const char *name, /* I - Name of attribute */
735 ipp_res_t units, /* I - Units for resolution */
736 int xres, /* I - X resolution */
737 int yres) /* I - Y resolution */
739 ipp_attribute_t *attr; /* New attribute */
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));
746 * Range check input...
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)
756 * Create the attribute...
759 if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RESOLUTION, 1)) == NULL)
762 attr->values[0].resolution.xres = xres;
763 attr->values[0].resolution.yres = yres;
764 attr->values[0].resolution.units = units;
771 * 'ippAddResolutions()' - Add resolution values to an IPP message.
773 * The @code ipp@ parameter refers to an IPP message previously created using
774 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
783 ipp_attribute_t * /* O - New attribute */
784 ippAddResolutions(ipp_t *ipp, /* I - IPP message */
785 ipp_tag_t group, /* I - IPP group */
786 const char *name, /* I - Name of attribute */
787 int num_values,/* I - Number of values */
788 ipp_res_t units, /* I - Units for resolution */
789 const int *xres, /* I - X resolutions */
790 const int *yres) /* I - Y resolutions */
792 int i; /* Looping var */
793 ipp_attribute_t *attr; /* New attribute */
794 _ipp_value_t *value; /* Current value */
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));
800 * Range check input...
803 if (!ipp || !name || group < IPP_TAG_ZERO ||
804 group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
806 units < IPP_RES_PER_INCH || units > IPP_RES_PER_CM)
810 * Create the attribute...
813 if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RESOLUTION, num_values)) == NULL)
818 for (i = num_values, value = attr->values;
822 value->resolution.xres = *xres++;
823 value->resolution.yres = *yres++;
824 value->resolution.units = units;
833 * 'ippAddSeparator()' - Add a group separator to an IPP message.
835 * The @code ipp@ parameter refers to an IPP message previously created using
836 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
839 ipp_attribute_t * /* O - New attribute */
840 ippAddSeparator(ipp_t *ipp) /* I - IPP message */
842 DEBUG_printf(("ippAddSeparator(ipp=%p)", (void *)ipp));
845 * Range check input...
852 * Create the attribute...
855 return (ipp_add_attr(ipp, NULL, IPP_TAG_ZERO, IPP_TAG_ZERO, 0));
860 * 'ippAddString()' - Add a language-encoded string to an IPP message.
862 * The @code ipp@ parameter refers to an IPP message previously created using
863 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
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@).
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.
882 ipp_attribute_t * /* O - New attribute */
883 ippAddString(ipp_t *ipp, /* I - IPP message */
884 ipp_tag_t group, /* I - IPP group */
885 ipp_tag_t value_tag, /* I - Type of attribute */
886 const char *name, /* I - Name of attribute */
887 const char *language, /* I - Language code */
888 const char *value) /* I - Value */
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 */
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));
899 * Range check input...
902 temp_tag = (ipp_tag_t)((int)value_tag & IPP_TAG_CUPS_MASK);
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)
911 if ((temp_tag == IPP_TAG_TEXTLANG || temp_tag == IPP_TAG_NAMELANG)
912 != (language != NULL))
915 if (!ipp || !name || group < IPP_TAG_ZERO ||
916 group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
921 * See if we need to map charset, language, or locale values...
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 */
935 * Create the attribute...
938 if ((attr = ipp_add_attr(ipp, name, group, value_tag, 1)) == NULL)
942 * Initialize the attribute data...
945 if ((int)value_tag & IPP_TAG_CUPS_CONST)
947 attr->values[0].string.language = (char *)language;
948 attr->values[0].string.text = (char *)value;
953 attr->values[0].string.language = _cupsStrAlloc(ipp_lang_code(language, code,
958 if (value_tag == IPP_TAG_CHARSET)
959 attr->values[0].string.text = _cupsStrAlloc(ipp_get_code(value, code,
961 else if (value_tag == IPP_TAG_LANGUAGE)
962 attr->values[0].string.text = _cupsStrAlloc(ipp_lang_code(value, code,
965 attr->values[0].string.text = _cupsStrAlloc(value);
974 * 'ippAddStringf()' - Add a formatted string to an IPP message.
976 * The @code ipp@ parameter refers to an IPP message previously created using
977 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
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@).
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
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.
1002 * @since CUPS 1.7/macOS 10.9@
1005 ipp_attribute_t * /* O - New attribute */
1006 ippAddStringf(ipp_t *ipp, /* I - IPP message */
1007 ipp_tag_t group, /* I - IPP group */
1008 ipp_tag_t value_tag, /* I - Type of attribute */
1009 const char *name, /* I - Name of attribute */
1010 const char *language, /* I - Language code (@code NULL@ for default) */
1011 const char *format, /* I - Printf-style format string */
1012 ...) /* I - Additional arguments as needed */
1014 ipp_attribute_t *attr; /* New attribute */
1015 va_list ap; /* Argument pointer */
1018 va_start(ap, format);
1019 attr = ippAddStringfv(ipp, group, value_tag, name, language, format, ap);
1027 * 'ippAddStringfv()' - Add a formatted string to an IPP message.
1029 * The @code ipp@ parameter refers to an IPP message previously created using
1030 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
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@).
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
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.
1055 * @since CUPS 1.7/macOS 10.9@
1058 ipp_attribute_t * /* O - New attribute */
1059 ippAddStringfv(ipp_t *ipp, /* I - IPP message */
1060 ipp_tag_t group, /* I - IPP group */
1061 ipp_tag_t value_tag, /* I - Type of attribute */
1062 const char *name, /* I - Name of attribute */
1063 const char *language, /* I - Language code (@code NULL@ for default) */
1064 const char *format, /* I - Printf-style format string */
1065 va_list ap) /* I - Additional arguments */
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 */
1074 * Range check input...
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 ||
1084 if ((value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_NAMELANG)
1085 != (language != NULL))
1089 * Format the string...
1092 if (!strcmp(format, "%s"))
1095 * Optimize the simple case...
1098 const char *s = va_arg(ap, char *);
1103 bytes = (ssize_t)strlen(s);
1104 strlcpy(buffer, s, sizeof(buffer));
1109 * Do a full formatting of the message...
1112 if ((bytes = vsnprintf(buffer, sizeof(buffer), format, ap)) < 0)
1117 * Limit the length of the string...
1124 case IPP_TAG_TEXTLANG :
1125 max_bytes = IPP_MAX_TEXT;
1129 case IPP_TAG_NAMELANG :
1130 max_bytes = IPP_MAX_NAME;
1133 case IPP_TAG_CHARSET :
1134 max_bytes = IPP_MAX_CHARSET;
1137 case IPP_TAG_KEYWORD :
1138 max_bytes = IPP_MAX_KEYWORD;
1141 case IPP_TAG_LANGUAGE :
1142 max_bytes = IPP_MAX_LANGUAGE;
1145 case IPP_TAG_MIMETYPE :
1146 max_bytes = IPP_MAX_MIMETYPE;
1150 max_bytes = IPP_MAX_URI;
1153 case IPP_TAG_URISCHEME :
1154 max_bytes = IPP_MAX_URISCHEME;
1158 if (bytes >= max_bytes)
1160 char *bufmax, /* Buffer at max_bytes */
1161 *bufptr; /* Pointer into buffer */
1163 bufptr = buffer + strlen(buffer) - 1;
1164 bufmax = buffer + max_bytes - 1;
1166 while (bufptr > bufmax)
1170 while ((*bufptr & 0xc0) == 0x80 && bufptr > buffer)
1181 * Add the formatted string and return...
1184 return (ippAddString(ipp, group, value_tag, name, language, buffer));
1189 * 'ippAddStrings()' - Add language-encoded strings to an IPP message.
1191 * The @code ipp@ parameter refers to an IPP message previously created using
1192 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
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@).
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@).
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.
1211 ipp_attribute_t * /* O - New attribute */
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 */
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 */
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));
1231 * Range check input...
1234 temp_tag = (ipp_tag_t)((int)value_tag & IPP_TAG_CUPS_MASK);
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 ||
1244 if ((temp_tag == IPP_TAG_TEXTLANG || temp_tag == IPP_TAG_NAMELANG)
1245 != (language != NULL))
1248 if (!ipp || !name || group < IPP_TAG_ZERO ||
1249 group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
1255 * See if we need to map charset, language, or locale values...
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))
1263 for (i = 0; i < num_values; i ++)
1264 if (strcmp(values[i], ipp_get_code(values[i], code, sizeof(code))))
1266 value_tag = temp_tag; /* Don't do a fast copy */
1270 else if (values && value_tag == (ipp_tag_t)(IPP_TAG_LANGUAGE | IPP_TAG_CUPS_CONST))
1272 for (i = 0; i < num_values; i ++)
1273 if (strcmp(values[i], ipp_lang_code(values[i], code, sizeof(code))))
1275 value_tag = temp_tag; /* Don't do a fast copy */
1281 * Create the attribute...
1284 if ((attr = ipp_add_attr(ipp, name, group, value_tag, num_values)) == NULL)
1288 * Initialize the attribute data...
1291 for (i = num_values, value = attr->values;
1297 if (value == attr->values)
1299 if ((int)value_tag & IPP_TAG_CUPS_CONST)
1300 value->string.language = (char *)language;
1302 value->string.language = _cupsStrAlloc(ipp_lang_code(language, code,
1306 value->string.language = attr->values[0].string.language;
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)));
1318 value->string.text = _cupsStrAlloc(*values++);
1327 * 'ippContainsInteger()' - Determine whether an attribute contains the
1328 * specified value or is within the list of ranges.
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
1334 * @since CUPS 1.7/macOS 10.9@
1337 int /* O - 1 on a match, 0 on no match */
1339 ipp_attribute_t *attr, /* I - Attribute */
1340 int value) /* I - Integer/enum value */
1342 int i; /* Looping var */
1343 _ipp_value_t *avalue; /* Current attribute value */
1347 * Range check input...
1353 if (attr->value_tag != IPP_TAG_INTEGER && attr->value_tag != IPP_TAG_ENUM &&
1354 attr->value_tag != IPP_TAG_RANGE)
1361 if (attr->value_tag == IPP_TAG_RANGE)
1363 for (i = attr->num_values, avalue = attr->values; i > 0; i --, avalue ++)
1364 if (value >= avalue->range.lower && value <= avalue->range.upper)
1369 for (i = attr->num_values, avalue = attr->values; i > 0; i --, avalue ++)
1370 if (value == avalue->integer)
1379 * 'ippContainsString()' - Determine whether an attribute contains the
1380 * specified string value.
1382 * Returns non-zero when the attribute contains a matching charset, keyword,
1383 * naturalLanguage, mimeMediaType, name, text, uri, or uriScheme value.
1385 * @since CUPS 1.7/macOS 10.9@
1388 int /* O - 1 on a match, 0 on no match */
1390 ipp_attribute_t *attr, /* I - Attribute */
1391 const char *value) /* I - String value */
1393 int i; /* Looping var */
1394 _ipp_value_t *avalue; /* Current attribute value */
1397 DEBUG_printf(("ippContainsString(attr=%p, value=\"%s\")", (void *)attr, value));
1400 * Range check input...
1403 if (!attr || !value)
1405 DEBUG_puts("1ippContainsString: Returning 0 (bad input)");
1413 DEBUG_printf(("1ippContainsString: attr %s, %s with %d values.",
1414 attr->name, ippTagString(attr->value_tag),
1417 switch (attr->value_tag & IPP_TAG_CUPS_MASK)
1419 case IPP_TAG_CHARSET :
1420 case IPP_TAG_KEYWORD :
1421 case IPP_TAG_LANGUAGE :
1423 case IPP_TAG_URISCHEME :
1424 for (i = attr->num_values, avalue = attr->values;
1428 DEBUG_printf(("1ippContainsString: value[%d]=\"%s\"",
1429 attr->num_values - i, avalue->string.text));
1431 if (!strcmp(value, avalue->string.text))
1433 DEBUG_puts("1ippContainsString: Returning 1 (match)");
1438 case IPP_TAG_MIMETYPE :
1440 case IPP_TAG_NAMELANG :
1442 case IPP_TAG_TEXTLANG :
1443 for (i = attr->num_values, avalue = attr->values;
1447 DEBUG_printf(("1ippContainsString: value[%d]=\"%s\"",
1448 attr->num_values - i, avalue->string.text));
1450 if (!_cups_strcasecmp(value, avalue->string.text))
1452 DEBUG_puts("1ippContainsString: Returning 1 (match)");
1461 DEBUG_puts("1ippContainsString: Returning 0 (no match)");
1468 * 'ippCopyAttribute()' - Copy an attribute.
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.
1475 * @since CUPS 1.6/macOS 10.8@
1479 ipp_attribute_t * /* O - New attribute */
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 */
1485 int i; /* Looping var */
1486 ipp_attribute_t *dstattr; /* Destination attribute */
1487 _ipp_value_t *srcval, /* Source value */
1488 *dstval; /* Destination value */
1491 DEBUG_printf(("ippCopyAttribute(dst=%p, srcattr=%p, quickcopy=%d)", (void *)dst, (void *)srcattr, quickcopy));
1494 * Range check input...
1497 if (!dst || !srcattr)
1504 quickcopy = quickcopy ? IPP_TAG_CUPS_CONST : 0;
1506 switch (srcattr->value_tag & ~IPP_TAG_CUPS_CONST)
1509 dstattr = ippAddSeparator(dst);
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);
1522 case IPP_TAG_INTEGER :
1524 dstattr = ippAddIntegers(dst, srcattr->group_tag, srcattr->value_tag,
1525 srcattr->name, srcattr->num_values, NULL);
1529 for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
1531 i --, srcval ++, dstval ++)
1532 dstval->integer = srcval->integer;
1535 case IPP_TAG_BOOLEAN :
1536 dstattr = ippAddBooleans(dst, srcattr->group_tag, srcattr->name,
1537 srcattr->num_values, NULL);
1541 for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
1543 i --, srcval ++, dstval ++)
1544 dstval->boolean = srcval->boolean;
1549 case IPP_TAG_KEYWORD :
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);
1563 for (i = srcattr->num_values, srcval = srcattr->values,
1564 dstval = dstattr->values;
1566 i --, srcval ++, dstval ++)
1567 dstval->string.text = srcval->string.text;
1569 else if (srcattr->value_tag & IPP_TAG_CUPS_CONST)
1571 for (i = srcattr->num_values, srcval = srcattr->values,
1572 dstval = dstattr->values;
1574 i --, srcval ++, dstval ++)
1575 dstval->string.text = _cupsStrAlloc(srcval->string.text);
1579 for (i = srcattr->num_values, srcval = srcattr->values,
1580 dstval = dstattr->values;
1582 i --, srcval ++, dstval ++)
1583 dstval->string.text = _cupsStrRetain(srcval->string.text);
1588 if (srcattr->num_values != 1)
1591 dstattr = ippAddDate(dst, srcattr->group_tag, srcattr->name,
1592 srcattr->values[0].date);
1595 case IPP_TAG_RESOLUTION :
1596 dstattr = ippAddResolutions(dst, srcattr->group_tag, srcattr->name,
1597 srcattr->num_values, IPP_RES_PER_INCH,
1602 for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
1604 i --, srcval ++, dstval ++)
1606 dstval->resolution.xres = srcval->resolution.xres;
1607 dstval->resolution.yres = srcval->resolution.yres;
1608 dstval->resolution.units = srcval->resolution.units;
1612 case IPP_TAG_RANGE :
1613 dstattr = ippAddRanges(dst, srcattr->group_tag, srcattr->name,
1614 srcattr->num_values, NULL, NULL);
1618 for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
1620 i --, srcval ++, dstval ++)
1622 dstval->range.lower = srcval->range.lower;
1623 dstval->range.upper = srcval->range.upper;
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);
1637 for (i = srcattr->num_values, srcval = srcattr->values,
1638 dstval = dstattr->values;
1640 i --, srcval ++, dstval ++)
1642 dstval->string.language = srcval->string.language;
1643 dstval->string.text = srcval->string.text;
1646 else if (srcattr->value_tag & IPP_TAG_CUPS_CONST)
1648 for (i = srcattr->num_values, srcval = srcattr->values,
1649 dstval = dstattr->values;
1651 i --, srcval ++, dstval ++)
1653 if (srcval == srcattr->values)
1654 dstval->string.language = _cupsStrAlloc(srcval->string.language);
1656 dstval->string.language = dstattr->values[0].string.language;
1658 dstval->string.text = _cupsStrAlloc(srcval->string.text);
1663 for (i = srcattr->num_values, srcval = srcattr->values,
1664 dstval = dstattr->values;
1666 i --, srcval ++, dstval ++)
1668 if (srcval == srcattr->values)
1669 dstval->string.language = _cupsStrRetain(srcval->string.language);
1671 dstval->string.language = dstattr->values[0].string.language;
1673 dstval->string.text = _cupsStrRetain(srcval->string.text);
1678 case IPP_TAG_BEGIN_COLLECTION :
1679 dstattr = ippAddCollections(dst, srcattr->group_tag, srcattr->name,
1680 srcattr->num_values, NULL);
1684 for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
1686 i --, srcval ++, dstval ++)
1688 dstval->collection = srcval->collection;
1689 srcval->collection->use ++;
1693 case IPP_TAG_STRING :
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);
1701 for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
1703 i --, srcval ++, dstval ++)
1705 dstval->unknown.length = srcval->unknown.length;
1707 if (dstval->unknown.length > 0)
1709 if ((dstval->unknown.data = malloc((size_t)dstval->unknown.length)) == NULL)
1710 dstval->unknown.length = 0;
1712 memcpy(dstval->unknown.data, srcval->unknown.data, (size_t)dstval->unknown.length);
1715 break; /* anti-compiler-warning-code */
1723 * 'ippCopyAttributes()' - Copy attributes from one IPP message to another.
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.
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
1735 * @since CUPS 1.6/macOS 10.8@
1738 int /* O - 1 on success, 0 on error */
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 */
1746 ipp_attribute_t *srcattr; /* Source attribute */
1749 DEBUG_printf(("ippCopyAttributes(dst=%p, src=%p, quickcopy=%d, cb=%p, context=%p)", (void *)dst, (void *)src, quickcopy, (void *)cb, context));
1752 * Range check input...
1759 * Loop through source attributes and copy as needed...
1762 for (srcattr = src->attrs; srcattr; srcattr = srcattr->next)
1763 if (!cb || (*cb)(context, dst, srcattr))
1764 if (!ippCopyAttribute(dst, srcattr, quickcopy))
1772 * 'ippDateToTime()' - Convert from RFC 2579 Date/Time format to time in
1776 time_t /* O - UNIX time value */
1777 ippDateToTime(const ipp_uchar_t *date) /* I - RFC 2579 date info */
1779 struct tm unixdate; /* UNIX date/time info */
1780 time_t t; /* Computed time */
1786 memset(&unixdate, 0, sizeof(unixdate));
1789 * RFC-2579 date/time format is:
1791 * Byte(s) Description
1792 * ------- -----------
1793 * 0-1 Year (0 to 65535)
1797 * 5 Minutes (0 to 59)
1798 * 6 Seconds (0 to 60, 60 = "leap second")
1799 * 7 Deciseconds (0 to 9)
1801 * 9 UTC hours (0 to 11)
1802 * 10 UTC minutes (0 to 59)
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];
1812 t = mktime(&unixdate);
1815 t += date[9] * 3600 + date[10] * 60;
1817 t -= date[9] * 3600 + date[10] * 60;
1824 * 'ippDelete()' - Delete an IPP message.
1828 ippDelete(ipp_t *ipp) /* I - IPP message */
1830 ipp_attribute_t *attr, /* Current attribute */
1831 *next; /* Next attribute */
1834 DEBUG_printf(("ippDelete(ipp=%p)", (void *)ipp));
1842 DEBUG_printf(("4debug_retain: %p IPP message (use=%d)", (void *)ipp, ipp->use));
1846 DEBUG_printf(("4debug_free: %p IPP message", (void *)ipp));
1848 for (attr = ipp->attrs; attr != NULL; attr = next)
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));
1854 ipp_free_values(attr, 0, attr->num_values);
1857 _cupsStrFree(attr->name);
1867 * 'ippDeleteAttribute()' - Delete a single attribute in an IPP message.
1869 * @since CUPS 1.1.19/macOS 10.3@
1874 ipp_t *ipp, /* I - IPP message */
1875 ipp_attribute_t *attr) /* I - Attribute to delete */
1877 ipp_attribute_t *current, /* Current attribute */
1878 *prev; /* Previous attribute */
1881 DEBUG_printf(("ippDeleteAttribute(ipp=%p, attr=%p(%s))", (void *)ipp, (void *)attr, attr ? attr->name : "(null)"));
1884 * Range check input...
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));
1893 * Find the attribute in the list...
1898 for (current = ipp->attrs, prev = NULL;
1900 prev = current, current = current->next)
1901 if (current == attr)
1904 * Found it, remove the attribute from the list...
1908 prev->next = current->next;
1910 ipp->attrs = current->next;
1912 if (current == ipp->last)
1923 * Free memory used by the attribute...
1926 ipp_free_values(attr, 0, attr->num_values);
1929 _cupsStrFree(attr->name);
1936 * 'ippDeleteValues()' - Delete values in an attribute.
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@.
1941 * The @code attr@ parameter may be modified as a result of setting the value.
1943 * Deleting all values in an attribute deletes the attribute.
1945 * @since CUPS 1.6/macOS 10.8@
1948 int /* O - 1 on success, 0 on failure */
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 */
1956 * Range check input...
1959 if (!ipp || !attr || !*attr ||
1960 element < 0 || element >= (*attr)->num_values || count <= 0 ||
1961 (element + count) >= (*attr)->num_values)
1965 * If we are deleting all values, just delete the attribute entirely.
1968 if (count == (*attr)->num_values)
1970 ippDeleteAttribute(ipp, *attr);
1976 * Otherwise free the values in question and return.
1979 ipp_free_values(*attr, element, count);
1986 * 'ippFindAttribute()' - Find a named attribute in a request.
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".
1993 ipp_attribute_t * /* O - Matching attribute */
1994 ippFindAttribute(ipp_t *ipp, /* I - IPP message */
1995 const char *name, /* I - Name of attribute */
1996 ipp_tag_t type) /* I - Type of attribute */
1998 DEBUG_printf(("2ippFindAttribute(ipp=%p, name=\"%s\", type=%02x(%s))", (void *)ipp, name, type, ippTagString(type)));
2004 * Reset the current pointer...
2007 ipp->current = NULL;
2011 * Search for the attribute...
2014 return (ippFindNextAttribute(ipp, name, type));
2019 * 'ippFindNextAttribute()' - Find the next named attribute in a request.
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".
2026 ipp_attribute_t * /* O - Matching attribute */
2027 ippFindNextAttribute(ipp_t *ipp, /* I - IPP message */
2028 const char *name, /* I - Name of attribute */
2029 ipp_tag_t type) /* I - Type of attribute */
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 */
2038 DEBUG_printf(("2ippFindNextAttribute(ipp=%p, name=\"%s\", type=%02x(%s))", (void *)ipp, name, type, ippTagString(type)));
2043 DEBUG_printf(("3ippFindNextAttribute: atend=%d", ipp->atend));
2048 if (strchr(name, '/'))
2051 * Search for child attribute...
2054 strlcpy(parent, name, sizeof(parent));
2055 if ((child = strchr(parent, '/')) == NULL)
2057 DEBUG_puts("3ippFindNextAttribute: Attribute name too long.");
2063 if (ipp->current && ipp->current->name && ipp->current->value_tag == IPP_TAG_BEGIN_COLLECTION && !strcmp(parent, ipp->current->name))
2065 while (ipp->curindex < ipp->current->num_values)
2067 if ((childattr = ippFindNextAttribute(ipp->current->values[ipp->curindex].collection, child, type)) != NULL)
2071 if (ipp->curindex < ipp->current->num_values && ipp->current->values[ipp->curindex].collection)
2072 ipp->current->values[ipp->curindex].collection->current = NULL;
2075 ipp->prev = ipp->current;
2076 ipp->current = ipp->current->next;
2089 ipp->current = ipp->attrs;
2094 attr = ipp->current;
2096 else if (ipp->current)
2098 ipp->prev = ipp->current;
2099 attr = ipp->current->next;
2107 for (; attr != NULL; ipp->prev = attr, attr = attr->next)
2109 DEBUG_printf(("4ippFindAttribute: attr=%p, name=\"%s\"", (void *)attr, attr->name));
2111 value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_CUPS_MASK);
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)))
2118 ipp->current = attr;
2120 if (name == parent && attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
2122 int i; /* Looping var */
2124 for (i = 0; i < attr->num_values; i ++)
2126 if ((childattr = ippFindAttribute(attr->values[i].collection, child, type)) != NULL)
2128 attr->values[0].collection->curindex = i;
2138 ipp->current = NULL;
2147 * 'ippFirstAttribute()' - Return the first attribute in the message.
2149 * @since CUPS 1.6/macOS 10.8@
2152 ipp_attribute_t * /* O - First attribute or @code NULL@ if none */
2153 ippFirstAttribute(ipp_t *ipp) /* I - IPP message */
2156 * Range check input...
2163 * Return the first attribute...
2166 return (ipp->current = ipp->attrs);
2171 * 'ippGetBoolean()' - Get a boolean value for an attribute.
2173 * The @code element@ parameter specifies which value to get from 0 to
2174 * @code ippGetCount(attr)@ - 1.
2176 * @since CUPS 1.6/macOS 10.8@
2179 int /* O - Boolean value or 0 on error */
2180 ippGetBoolean(ipp_attribute_t *attr, /* I - IPP attribute */
2181 int element) /* I - Value number (0-based) */
2184 * Range check input...
2187 if (!attr || attr->value_tag != IPP_TAG_BOOLEAN ||
2188 element < 0 || element >= attr->num_values)
2192 * Return the value...
2195 return (attr->values[element].boolean);
2200 * 'ippGetCollection()' - Get a collection value for an attribute.
2202 * The @code element@ parameter specifies which value to get from 0 to
2203 * @code ippGetCount(attr)@ - 1.
2205 * @since CUPS 1.6/macOS 10.8@
2208 ipp_t * /* O - Collection value or @code NULL@ on error */
2210 ipp_attribute_t *attr, /* I - IPP attribute */
2211 int element) /* I - Value number (0-based) */
2214 * Range check input...
2217 if (!attr || attr->value_tag != IPP_TAG_BEGIN_COLLECTION ||
2218 element < 0 || element >= attr->num_values)
2222 * Return the value...
2225 return (attr->values[element].collection);
2230 * 'ippGetCount()' - Get the number of values in an attribute.
2232 * @since CUPS 1.6/macOS 10.8@
2235 int /* O - Number of values or 0 on error */
2236 ippGetCount(ipp_attribute_t *attr) /* I - IPP attribute */
2239 * Range check input...
2246 * Return the number of values...
2249 return (attr->num_values);
2254 * 'ippGetDate()' - Get a dateTime value for an attribute.
2256 * The @code element@ parameter specifies which value to get from 0 to
2257 * @code ippGetCount(attr)@ - 1.
2259 * @since CUPS 1.6/macOS 10.8@
2262 const ipp_uchar_t * /* O - dateTime value or @code NULL@ */
2263 ippGetDate(ipp_attribute_t *attr, /* I - IPP attribute */
2264 int element) /* I - Value number (0-based) */
2267 * Range check input...
2270 if (!attr || attr->value_tag != IPP_TAG_DATE ||
2271 element < 0 || element >= attr->num_values)
2275 * Return the value...
2278 return (attr->values[element].date);
2283 * 'ippGetGroupTag()' - Get the group associated with an attribute.
2285 * @since CUPS 1.6/macOS 10.8@
2288 ipp_tag_t /* O - Group tag or @code IPP_TAG_ZERO@ on error */
2289 ippGetGroupTag(ipp_attribute_t *attr) /* I - IPP attribute */
2292 * Range check input...
2296 return (IPP_TAG_ZERO);
2299 * Return the group...
2302 return (attr->group_tag);
2307 * 'ippGetInteger()' - Get the integer/enum value for an attribute.
2309 * The @code element@ parameter specifies which value to get from 0 to
2310 * @code ippGetCount(attr)@ - 1.
2312 * @since CUPS 1.6/macOS 10.8@
2315 int /* O - Value or 0 on error */
2316 ippGetInteger(ipp_attribute_t *attr, /* I - IPP attribute */
2317 int element) /* I - Value number (0-based) */
2320 * Range check input...
2323 if (!attr || (attr->value_tag != IPP_TAG_INTEGER && attr->value_tag != IPP_TAG_ENUM) ||
2324 element < 0 || element >= attr->num_values)
2328 * Return the value...
2331 return (attr->values[element].integer);
2336 * 'ippGetName()' - Get the attribute name.
2338 * @since CUPS 1.6/macOS 10.8@
2341 const char * /* O - Attribute name or @code NULL@ for separators */
2342 ippGetName(ipp_attribute_t *attr) /* I - IPP attribute */
2345 * Range check input...
2352 * Return the name...
2355 return (attr->name);
2360 * 'ippGetOctetString()' - Get an octetString value from an IPP attribute.
2362 * The @code element@ parameter specifies which value to get from 0 to
2363 * @code ippGetCount(attr)@ - 1.
2365 * @since CUPS 1.7/macOS 10.9@
2368 void * /* O - Pointer to octetString data */
2370 ipp_attribute_t *attr, /* I - IPP attribute */
2371 int element, /* I - Value number (0-based) */
2372 int *datalen) /* O - Length of octetString data */
2375 * Range check input...
2378 if (!attr || attr->value_tag != IPP_TAG_STRING ||
2379 element < 0 || element >= attr->num_values)
2388 * Return the values...
2392 *datalen = attr->values[element].unknown.length;
2394 return (attr->values[element].unknown.data);
2399 * 'ippGetOperation()' - Get the operation ID in an IPP message.
2401 * @since CUPS 1.6/macOS 10.8@
2404 ipp_op_t /* O - Operation ID or 0 on error */
2405 ippGetOperation(ipp_t *ipp) /* I - IPP request message */
2408 * Range check input...
2412 return ((ipp_op_t)0);
2415 * Return the value...
2418 return (ipp->request.op.operation_id);
2423 * 'ippGetRange()' - Get a rangeOfInteger value from an attribute.
2425 * The @code element@ parameter specifies which value to get from 0 to
2426 * @code ippGetCount(attr)@ - 1.
2428 * @since CUPS 1.6/macOS 10.8@
2431 int /* O - Lower value of range or 0 */
2432 ippGetRange(ipp_attribute_t *attr, /* I - IPP attribute */
2433 int element, /* I - Value number (0-based) */
2434 int *uppervalue)/* O - Upper value of range */
2437 * Range check input...
2440 if (!attr || attr->value_tag != IPP_TAG_RANGE ||
2441 element < 0 || element >= attr->num_values)
2450 * Return the values...
2454 *uppervalue = attr->values[element].range.upper;
2456 return (attr->values[element].range.lower);
2461 * 'ippGetRequestId()' - Get the request ID from an IPP message.
2463 * @since CUPS 1.6/macOS 10.8@
2466 int /* O - Request ID or 0 on error */
2467 ippGetRequestId(ipp_t *ipp) /* I - IPP message */
2470 * Range check input...
2477 * Return the request ID...
2480 return (ipp->request.any.request_id);
2485 * 'ippGetResolution()' - Get a resolution value for an attribute.
2487 * The @code element@ parameter specifies which value to get from 0 to
2488 * @code ippGetCount(attr)@ - 1.
2490 * @since CUPS 1.6/macOS 10.8@
2493 int /* O - Horizontal/cross feed resolution or 0 */
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 */
2501 * Range check input...
2504 if (!attr || attr->value_tag != IPP_TAG_RESOLUTION ||
2505 element < 0 || element >= attr->num_values)
2511 *units = (ipp_res_t)0;
2517 * Return the value...
2521 *yres = attr->values[element].resolution.yres;
2524 *units = attr->values[element].resolution.units;
2526 return (attr->values[element].resolution.xres);
2531 * 'ippGetState()' - Get the IPP message state.
2533 * @since CUPS 1.6/macOS 10.8@
2536 ipp_state_t /* O - IPP message state value */
2537 ippGetState(ipp_t *ipp) /* I - IPP message */
2540 * Range check input...
2544 return (IPP_STATE_IDLE);
2547 * Return the value...
2550 return (ipp->state);
2555 * 'ippGetStatusCode()' - Get the status code from an IPP response or event message.
2557 * @since CUPS 1.6/macOS 10.8@
2560 ipp_status_t /* O - Status code in IPP message */
2561 ippGetStatusCode(ipp_t *ipp) /* I - IPP response or event message */
2564 * Range check input...
2568 return (IPP_STATUS_ERROR_INTERNAL);
2571 * Return the value...
2574 return (ipp->request.status.status_code);
2579 * 'ippGetString()' - Get the string and optionally the language code for an attribute.
2581 * The @code element@ parameter specifies which value to get from 0 to
2582 * @code ippGetCount(attr)@ - 1.
2584 * @since CUPS 1.6/macOS 10.8@
2588 ippGetString(ipp_attribute_t *attr, /* I - IPP attribute */
2589 int element, /* I - Value number (0-based) */
2590 const char **language)/* O - Language code (@code NULL@ for don't care) */
2592 ipp_tag_t tag; /* Value tag */
2596 * Range check input...
2599 tag = ippGetValueTag(attr);
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)))
2605 * Return the value...
2609 *language = attr->values[element].string.language;
2611 return (attr->values[element].string.text);
2616 * 'ippGetValueTag()' - Get the value tag for an attribute.
2618 * @since CUPS 1.6/macOS 10.8@
2621 ipp_tag_t /* O - Value tag or @code IPP_TAG_ZERO@ on error */
2622 ippGetValueTag(ipp_attribute_t *attr) /* I - IPP attribute */
2625 * Range check input...
2629 return (IPP_TAG_ZERO);
2632 * Return the value...
2635 return (attr->value_tag & IPP_TAG_CUPS_MASK);
2640 * 'ippGetVersion()' - Get the major and minor version number from an IPP message.
2642 * @since CUPS 1.6/macOS 10.8@
2645 int /* O - Major version number or 0 on error */
2646 ippGetVersion(ipp_t *ipp, /* I - IPP message */
2647 int *minor) /* O - Minor version number or @code NULL@ for don't care */
2650 * Range check input...
2662 * Return the value...
2666 *minor = ipp->request.any.version[1];
2668 return (ipp->request.any.version[0]);
2673 * 'ippLength()' - Compute the length of an IPP message.
2676 size_t /* O - Size of IPP message */
2677 ippLength(ipp_t *ipp) /* I - IPP message */
2679 return (ipp_length(ipp, 0));
2684 * 'ippNextAttribute()' - Return the next attribute in the message.
2686 * @since CUPS 1.6/macOS 10.8@
2689 ipp_attribute_t * /* O - Next attribute or @code NULL@ if none */
2690 ippNextAttribute(ipp_t *ipp) /* I - IPP message */
2693 * Range check input...
2696 if (!ipp || !ipp->current)
2700 * Return the next attribute...
2703 return (ipp->current = ipp->current->next);
2708 * 'ippNew()' - Allocate a new IPP message.
2711 ipp_t * /* O - New IPP message */
2714 ipp_t *temp; /* New IPP message */
2715 _cups_globals_t *cg = _cupsGlobals();
2719 DEBUG_puts("ippNew()");
2721 if ((temp = (ipp_t *)calloc(1, sizeof(ipp_t))) != NULL)
2724 * Set default version - usually 2.0...
2727 DEBUG_printf(("4debug_alloc: %p IPP message", (void *)temp));
2729 if (cg->server_version == 0)
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);
2737 DEBUG_printf(("1ippNew: Returning %p", (void *)temp));
2744 * 'ippNewRequest()' - Allocate a new IPP request message.
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.
2750 * @since CUPS 1.2/macOS 10.5@
2753 ipp_t * /* O - IPP request message */
2754 ippNewRequest(ipp_op_t op) /* I - Operation code */
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 */
2763 DEBUG_printf(("ippNewRequest(op=%02x(%s))", op, ippOpString(op)));
2766 * Create a new IPP message...
2769 if ((request = ippNew()) == NULL)
2773 * Set the operation and request ID...
2776 _cupsMutexLock(&request_mutex);
2778 request->request.op.operation_id = op;
2779 request->request.op.request_id = ++request_id;
2781 _cupsMutexUnlock(&request_mutex);
2784 * Use UTF-8 as the character set...
2787 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
2788 "attributes-charset", NULL, "utf-8");
2791 * Get the language from the current locale...
2794 language = cupsLangDefault();
2796 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
2797 "attributes-natural-language", NULL, language->language);
2800 * Return the new request...
2808 * 'ippNewResponse()' - Allocate a new IPP response message.
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,
2817 * @since CUPS 1.7/macOS 10.9@
2820 ipp_t * /* O - IPP response message */
2821 ippNewResponse(ipp_t *request) /* I - IPP request message */
2823 ipp_t *response; /* IPP response message */
2824 ipp_attribute_t *attr; /* Current attribute */
2828 * Range check input...
2835 * Create a new IPP message...
2838 if ((response = ippNew()) == NULL)
2842 * Copy the request values over to the response...
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;
2850 * The first attribute MUST be attributes-charset...
2853 attr = request->attrs;
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)
2861 * Copy charset from request...
2864 ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
2865 "attributes-charset", NULL, attr->values[0].string.text);
2870 * Use "utf-8" as the default...
2873 ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
2874 "attributes-charset", NULL, "utf-8");
2878 * Then attributes-natural-language...
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)
2891 * Copy language from request...
2894 ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
2895 "attributes-natural-language", NULL,
2896 attr->values[0].string.text);
2901 * Use the language from the current locale...
2904 cups_lang_t *language = cupsLangDefault();
2905 /* Current locale */
2907 ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
2908 "attributes-natural-language", NULL, language->language);
2916 * 'ippRead()' - Read data for an IPP message from a HTTP connection.
2919 ipp_state_t /* O - Current state */
2920 ippRead(http_t *http, /* I - HTTP connection */
2921 ipp_t *ipp) /* I - IPP data */
2923 DEBUG_printf(("ippRead(http=%p, ipp=%p), data_remaining=" CUPS_LLFMT, (void *)http, (void *)ipp, CUPS_LLCAST (http ? http->data_remaining : -1)));
2926 return (IPP_STATE_ERROR);
2928 DEBUG_printf(("2ippRead: http->state=%d, http->used=%d", http->state, http->used));
2930 return (ippReadIO(http, (ipp_iocb_t)ipp_read_http, http->blocking, NULL,
2936 * 'ippReadFile()' - Read data for an IPP message from a file.
2938 * @since CUPS 1.1.19/macOS 10.3@
2941 ipp_state_t /* O - Current state */
2942 ippReadFile(int fd, /* I - HTTP data */
2943 ipp_t *ipp) /* I - IPP data */
2945 DEBUG_printf(("ippReadFile(fd=%d, ipp=%p)", fd, (void *)ipp));
2947 return (ippReadIO(&fd, (ipp_iocb_t)ipp_read_file, 1, NULL, ipp));
2952 * 'ippReadIO()' - Read data for an IPP message.
2954 * @since CUPS 1.2/macOS 10.5@
2957 ipp_state_t /* O - Current state */
2958 ippReadIO(void *src, /* I - Data source */
2959 ipp_iocb_t cb, /* I - Read callback function */
2960 int blocking, /* I - Use blocking IO? */
2961 ipp_t *parent, /* I - Parent request, if any */
2962 ipp_t *ipp) /* I - IPP data */
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 */
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));
2979 return (IPP_STATE_ERROR);
2981 if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL)
2983 DEBUG_puts("1ippReadIO: Unable to get read buffer.");
2984 return (IPP_STATE_ERROR);
2989 case IPP_STATE_IDLE :
2990 ipp->state ++; /* Avoid common problem... */
2992 case IPP_STATE_HEADER :
2996 * Get the request header...
2999 if ((*cb)(src, buffer, 8) < 8)
3001 DEBUG_puts("1ippReadIO: Unable to read header.");
3002 _cupsBufferRelease((char *)buffer);
3003 return (IPP_STATE_ERROR);
3007 * Then copy the request header over...
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];
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));
3023 ipp->state = IPP_STATE_ATTRIBUTE;
3024 ipp->current = NULL;
3025 ipp->curtag = IPP_TAG_ZERO;
3026 ipp->prev = ipp->last;
3029 * If blocking is disabled, stop here...
3035 case IPP_STATE_ATTRIBUTE :
3038 if ((*cb)(src, buffer, 1) < 1)
3040 DEBUG_puts("1ippReadIO: Callback returned EOF/error");
3041 _cupsBufferRelease((char *)buffer);
3042 return (IPP_STATE_ERROR);
3045 DEBUG_printf(("2ippReadIO: ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev));
3048 * Read this attribute...
3051 tag = (ipp_tag_t)buffer[0];
3052 if (tag == IPP_TAG_EXTENSION)
3055 * Read 32-bit "extension" tag...
3058 if ((*cb)(src, buffer, 4) < 1)
3060 DEBUG_puts("1ippReadIO: Callback returned EOF/error");
3061 _cupsBufferRelease((char *)buffer);
3062 return (IPP_STATE_ERROR);
3065 tag = (ipp_tag_t)((((((buffer[0] << 8) | buffer[1]) << 8) |
3066 buffer[2]) << 8) | buffer[3]);
3068 if (tag & IPP_TAG_CUPS_CONST)
3071 * Fail if the high bit is set in the tag...
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);
3081 if (tag == IPP_TAG_END)
3084 * No more attributes left...
3087 DEBUG_puts("2ippReadIO: IPP_TAG_END.");
3089 ipp->state = IPP_STATE_DATA;
3092 else if (tag < IPP_TAG_UNSUPPORTED_VALUE)
3095 * Group tag... Set the current group and continue...
3098 if (ipp->curtag == tag)
3099 ipp->prev = ippAddSeparator(ipp);
3100 else if (ipp->current)
3101 ipp->prev = ipp->current;
3104 ipp->current = NULL;
3105 DEBUG_printf(("2ippReadIO: group tag=%x(%s), ipp->prev=%p", tag, ippTagString(tag), (void *)ipp->prev));
3109 DEBUG_printf(("2ippReadIO: value tag=%x(%s)", tag,
3110 ippTagString(tag)));
3116 if ((*cb)(src, buffer, 2) < 2)
3118 DEBUG_puts("1ippReadIO: unable to read name length.");
3119 _cupsBufferRelease((char *)buffer);
3120 return (IPP_STATE_ERROR);
3123 n = (buffer[0] << 8) | buffer[1];
3125 if (n >= IPP_BUF_SIZE)
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);
3133 DEBUG_printf(("2ippReadIO: name length=%d", n));
3135 if (n == 0 && tag != IPP_TAG_MEMBERNAME &&
3136 tag != IPP_TAG_END_COLLECTION)
3139 * More values for current attribute...
3142 if (ipp->current == NULL)
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);
3150 attr = ipp->current;
3151 value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_CUPS_MASK);
3154 * Make sure we aren't adding a new value of a different
3158 if (value_tag == IPP_TAG_ZERO)
3161 * Setting the value of a collection member...
3164 attr->value_tag = tag;
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))
3172 * String values can sometimes come across in different
3173 * forms; accept sets of differing values...
3176 if (tag != IPP_TAG_TEXTLANG && tag != IPP_TAG_NAMELANG &&
3177 (tag < IPP_TAG_TEXT || tag > IPP_TAG_MIMETYPE) &&
3178 tag != IPP_TAG_NOVALUE)
3180 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
3181 _("IPP 1setOf attribute with incompatible value "
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);
3190 if (value_tag != tag)
3192 DEBUG_printf(("1ippReadIO: Converting %s attribute from %s to %s.",
3193 attr->name, ippTagString(value_tag), ippTagString(tag)));
3194 ippSetValueTag(ipp, &attr, tag);
3197 else if (value_tag == IPP_TAG_INTEGER ||
3198 value_tag == IPP_TAG_RANGE)
3201 * Integer and rangeOfInteger values can sometimes be mixed; accept
3202 * sets of differing values...
3205 if (tag != IPP_TAG_INTEGER && tag != IPP_TAG_RANGE)
3207 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
3208 _("IPP 1setOf attribute with incompatible value "
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);
3217 if (value_tag == IPP_TAG_INTEGER && tag == IPP_TAG_RANGE)
3220 * Convert integer values to rangeOfInteger values...
3223 DEBUG_printf(("1ippReadIO: Converting %s attribute to "
3224 "rangeOfInteger.", attr->name));
3225 ippSetValueTag(ipp, &attr, IPP_TAG_RANGE);
3228 else if (value_tag != tag)
3230 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
3231 _("IPP 1setOf attribute with incompatible value "
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);
3241 * Finally, reallocate the attribute array as needed...
3244 if ((value = ipp_set_value(ipp, &attr, attr->num_values)) == NULL)
3246 _cupsBufferRelease((char *)buffer);
3247 return (IPP_STATE_ERROR);
3250 else if (tag == IPP_TAG_MEMBERNAME)
3253 * Name must be length 0!
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);
3265 ipp->prev = ipp->current;
3267 attr = ipp->current = ipp_add_attr(ipp, NULL, ipp->curtag, IPP_TAG_ZERO, 1);
3270 _cupsSetHTTPError(HTTP_STATUS_ERROR);
3271 DEBUG_puts("1ippReadIO: unable to allocate attribute.");
3272 _cupsBufferRelease((char *)buffer);
3273 return (IPP_STATE_ERROR);
3276 DEBUG_printf(("2ippReadIO: membername, ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev));
3278 value = attr->values;
3280 else if (tag != IPP_TAG_END_COLLECTION)
3283 * New attribute; read the name and add it...
3286 if ((*cb)(src, buffer, (size_t)n) < n)
3288 DEBUG_puts("1ippReadIO: unable to read name.");
3289 _cupsBufferRelease((char *)buffer);
3290 return (IPP_STATE_ERROR);
3296 ipp->prev = ipp->current;
3298 if ((attr = ipp->current = ipp_add_attr(ipp, (char *)buffer, ipp->curtag, tag,
3301 _cupsSetHTTPError(HTTP_STATUS_ERROR);
3302 DEBUG_puts("1ippReadIO: unable to allocate attribute.");
3303 _cupsBufferRelease((char *)buffer);
3304 return (IPP_STATE_ERROR);
3307 DEBUG_printf(("2ippReadIO: name=\"%s\", ipp->current=%p, ipp->prev=%p", buffer, (void *)ipp->current, (void *)ipp->prev));
3309 value = attr->values;
3317 if ((*cb)(src, buffer, 2) < 2)
3319 DEBUG_puts("1ippReadIO: unable to read value length.");
3320 _cupsBufferRelease((char *)buffer);
3321 return (IPP_STATE_ERROR);
3324 n = (buffer[0] << 8) | buffer[1];
3325 DEBUG_printf(("2ippReadIO: value length=%d", n));
3327 if (n >= IPP_BUF_SIZE)
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);
3338 case IPP_TAG_INTEGER :
3342 if (tag == IPP_TAG_INTEGER)
3343 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
3344 _("IPP integer value not 4 bytes."), 1);
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);
3353 if ((*cb)(src, buffer, 4) < 4)
3355 DEBUG_puts("1ippReadIO: Unable to read integer value.");
3356 _cupsBufferRelease((char *)buffer);
3357 return (IPP_STATE_ERROR);
3360 n = (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
3363 if (attr->value_tag == IPP_TAG_RANGE)
3364 value->range.lower = value->range.upper = n;
3369 case IPP_TAG_BOOLEAN :
3372 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP boolean value not 1 byte."),
3374 DEBUG_printf(("1ippReadIO: bad boolean value length %d.", n));
3375 _cupsBufferRelease((char *)buffer);
3376 return (IPP_STATE_ERROR);
3379 if ((*cb)(src, buffer, 1) < 1)
3381 DEBUG_puts("1ippReadIO: Unable to read boolean value.");
3382 _cupsBufferRelease((char *)buffer);
3383 return (IPP_STATE_ERROR);
3386 value->boolean = (char)buffer[0];
3389 case IPP_TAG_NOVALUE :
3390 case IPP_TAG_NOTSETTABLE :
3391 case IPP_TAG_DELETEATTR :
3392 case IPP_TAG_ADMINDEFINE :
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...
3399 if (attr->value_tag == tag)
3404 attr->value_tag = IPP_TAG_TEXT;
3409 case IPP_TAG_KEYWORD :
3411 case IPP_TAG_URISCHEME :
3412 case IPP_TAG_CHARSET :
3413 case IPP_TAG_LANGUAGE :
3414 case IPP_TAG_MIMETYPE :
3417 if ((*cb)(src, buffer, (size_t)n) < n)
3419 DEBUG_puts("1ippReadIO: unable to read string value.");
3420 _cupsBufferRelease((char *)buffer);
3421 return (IPP_STATE_ERROR);
3426 value->string.text = _cupsStrAlloc((char *)buffer);
3427 DEBUG_printf(("2ippReadIO: value=\"%s\"", value->string.text));
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);
3439 if ((*cb)(src, value->date, 11) < 11)
3441 DEBUG_puts("1ippReadIO: Unable to read date value.");
3442 _cupsBufferRelease((char *)buffer);
3443 return (IPP_STATE_ERROR);
3447 case IPP_TAG_RESOLUTION :
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);
3457 if ((*cb)(src, buffer, 9) < 9)
3459 DEBUG_puts("1ippReadIO: Unable to read resolution value.");
3460 _cupsBufferRelease((char *)buffer);
3461 return (IPP_STATE_ERROR);
3464 value->resolution.xres =
3465 (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
3467 value->resolution.yres =
3468 (((((buffer[4] << 8) | buffer[5]) << 8) | buffer[6]) << 8) |
3470 value->resolution.units =
3471 (ipp_res_t)buffer[8];
3474 case IPP_TAG_RANGE :
3477 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
3478 _("IPP rangeOfInteger value not 8 bytes."), 1);
3479 DEBUG_printf(("1ippReadIO: bad rangeOfInteger value length "
3481 _cupsBufferRelease((char *)buffer);
3482 return (IPP_STATE_ERROR);
3485 if ((*cb)(src, buffer, 8) < 8)
3487 DEBUG_puts("1ippReadIO: Unable to read range value.");
3488 _cupsBufferRelease((char *)buffer);
3489 return (IPP_STATE_ERROR);
3492 value->range.lower =
3493 (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) |
3495 value->range.upper =
3496 (((((buffer[4] << 8) | buffer[5]) << 8) | buffer[6]) << 8) |
3500 case IPP_TAG_TEXTLANG :
3501 case IPP_TAG_NAMELANG :
3504 if (tag == IPP_TAG_TEXTLANG)
3505 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
3506 _("IPP textWithLanguage value less than "
3507 "minimum 4 bytes."), 1);
3509 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
3510 _("IPP nameWithLanguage value less than "
3511 "minimum 4 bytes."), 1);
3512 DEBUG_printf(("1ippReadIO: bad stringWithLanguage value "
3514 _cupsBufferRelease((char *)buffer);
3515 return (IPP_STATE_ERROR);
3518 if ((*cb)(src, buffer, (size_t)n) < n)
3520 DEBUG_puts("1ippReadIO: Unable to read string w/language "
3522 _cupsBufferRelease((char *)buffer);
3523 return (IPP_STATE_ERROR);
3529 * text-with-language and name-with-language are composite
3538 n = (bufptr[0] << 8) | bufptr[1];
3540 if ((bufptr + 2 + n) >= (buffer + IPP_BUF_SIZE) || n >= (int)sizeof(string))
3542 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
3543 _("IPP language length overflows value."), 1);
3544 DEBUG_printf(("1ippReadIO: bad language value length %d.",
3546 _cupsBufferRelease((char *)buffer);
3547 return (IPP_STATE_ERROR);
3549 else if (n >= IPP_MAX_LANGUAGE)
3551 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
3552 _("IPP language length too large."), 1);
3553 DEBUG_printf(("1ippReadIO: bad language value length %d.",
3555 _cupsBufferRelease((char *)buffer);
3556 return (IPP_STATE_ERROR);
3559 memcpy(string, bufptr + 2, (size_t)n);
3562 value->string.language = _cupsStrAlloc((char *)string);
3565 n = (bufptr[0] << 8) | bufptr[1];
3567 if ((bufptr + 2 + n) >= (buffer + IPP_BUF_SIZE))
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);
3576 bufptr[2 + n] = '\0';
3577 value->string.text = _cupsStrAlloc((char *)bufptr + 2);
3580 case IPP_TAG_BEGIN_COLLECTION :
3582 * Oh, boy, here comes a collection value, so read it...
3585 value->collection = ippNew();
3589 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
3590 _("IPP begCollection value not 0 bytes."), 1);
3591 DEBUG_puts("1ippReadIO: begCollection tag with value length "
3593 _cupsBufferRelease((char *)buffer);
3594 return (IPP_STATE_ERROR);
3597 if (ippReadIO(src, cb, 1, ipp, value->collection) == IPP_STATE_ERROR)
3599 DEBUG_puts("1ippReadIO: Unable to read collection value.");
3600 _cupsBufferRelease((char *)buffer);
3601 return (IPP_STATE_ERROR);
3605 case IPP_TAG_END_COLLECTION :
3606 _cupsBufferRelease((char *)buffer);
3610 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
3611 _("IPP endCollection value not 0 bytes."), 1);
3612 DEBUG_puts("1ippReadIO: endCollection tag with value length "
3614 return (IPP_STATE_ERROR);
3617 DEBUG_puts("1ippReadIO: endCollection tag...");
3618 return (ipp->state = IPP_STATE_DATA);
3620 case IPP_TAG_MEMBERNAME :
3622 * The value the name of the member in the collection, which
3623 * we need to carry over...
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);
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);
3642 else if ((*cb)(src, buffer, (size_t)n) < n)
3644 DEBUG_puts("1ippReadIO: Unable to read member name value.");
3645 _cupsBufferRelease((char *)buffer);
3646 return (IPP_STATE_ERROR);
3650 attr->name = _cupsStrAlloc((char *)buffer);
3653 * Since collection members are encoded differently than
3654 * regular attributes, make sure we don't start with an
3658 attr->num_values --;
3660 DEBUG_printf(("2ippReadIO: member name=\"%s\"", attr->name));
3663 default : /* Other unsupported values */
3664 if (tag == IPP_TAG_STRING && n > IPP_MAX_LENGTH)
3666 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
3667 _("IPP octetString length too large."), 1);
3668 DEBUG_printf(("1ippReadIO: bad octetString value length %d.",
3670 _cupsBufferRelease((char *)buffer);
3671 return (IPP_STATE_ERROR);
3674 value->unknown.length = n;
3678 if ((value->unknown.data = malloc((size_t)n)) == NULL)
3680 _cupsSetHTTPError(HTTP_STATUS_ERROR);
3681 DEBUG_puts("1ippReadIO: Unable to allocate value");
3682 _cupsBufferRelease((char *)buffer);
3683 return (IPP_STATE_ERROR);
3686 if ((*cb)(src, value->unknown.data, (size_t)n) < n)
3688 DEBUG_puts("1ippReadIO: Unable to read unsupported value.");
3689 _cupsBufferRelease((char *)buffer);
3690 return (IPP_STATE_ERROR);
3694 value->unknown.data = NULL;
3699 * If blocking is disabled, stop here...
3707 case IPP_STATE_DATA :
3711 break; /* anti-compiler-warning-code */
3714 DEBUG_printf(("1ippReadIO: returning ipp->state=%d.", ipp->state));
3715 _cupsBufferRelease((char *)buffer);
3717 return (ipp->state);
3722 * 'ippSetBoolean()' - Set a boolean value in an attribute.
3724 * The @code ipp@ parameter refers to an IPP message previously created using
3725 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
3727 * The @code attr@ parameter may be modified as a result of setting the value.
3729 * The @code element@ parameter specifies which value to set from 0 to
3730 * @code ippGetCount(attr)@.
3732 * @since CUPS 1.6/macOS 10.8@
3735 int /* O - 1 on success, 0 on failure */
3736 ippSetBoolean(ipp_t *ipp, /* I - IPP message */
3737 ipp_attribute_t **attr, /* IO - IPP attribute */
3738 int element, /* I - Value number (0-based) */
3739 int boolvalue)/* I - Boolean value */
3741 _ipp_value_t *value; /* Current value */
3745 * Range check input...
3748 if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BOOLEAN ||
3749 element < 0 || element > (*attr)->num_values)
3753 * Set the value and return...
3756 if ((value = ipp_set_value(ipp, attr, element)) != NULL)
3757 value->boolean = (char)boolvalue;
3759 return (value != NULL);
3764 * 'ippSetCollection()' - Set a collection value in an attribute.
3766 * The @code ipp@ parameter refers to an IPP message previously created using
3767 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
3769 * The @code attr@ parameter may be modified as a result of setting the value.
3771 * The @code element@ parameter specifies which value to set from 0 to
3772 * @code ippGetCount(attr)@.
3774 * @since CUPS 1.6/macOS 10.8@
3777 int /* O - 1 on success, 0 on failure */
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 */
3784 _ipp_value_t *value; /* Current value */
3788 * Range check input...
3791 if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BEGIN_COLLECTION ||
3792 element < 0 || element > (*attr)->num_values || !colvalue)
3796 * Set the value and return...
3799 if ((value = ipp_set_value(ipp, attr, element)) != NULL)
3801 if (value->collection)
3802 ippDelete(value->collection);
3804 value->collection = colvalue;
3808 return (value != NULL);
3813 * 'ippSetDate()' - Set a dateTime value in an attribute.
3815 * The @code ipp@ parameter refers to an IPP message previously created using
3816 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
3818 * The @code attr@ parameter may be modified as a result of setting the value.
3820 * The @code element@ parameter specifies which value to set from 0 to
3821 * @code ippGetCount(attr)@.
3823 * @since CUPS 1.6/macOS 10.8@
3826 int /* O - 1 on success, 0 on failure */
3827 ippSetDate(ipp_t *ipp, /* I - IPP message */
3828 ipp_attribute_t **attr, /* IO - IPP attribute */
3829 int element, /* I - Value number (0-based) */
3830 const ipp_uchar_t *datevalue)/* I - dateTime value */
3832 _ipp_value_t *value; /* Current value */
3836 * Range check input...
3839 if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_DATE ||
3840 element < 0 || element > (*attr)->num_values || !datevalue)
3844 * Set the value and return...
3847 if ((value = ipp_set_value(ipp, attr, element)) != NULL)
3848 memcpy(value->date, datevalue, sizeof(value->date));
3850 return (value != NULL);
3855 * 'ippSetGroupTag()' - Set the group tag of an attribute.
3857 * The @code ipp@ parameter refers to an IPP message previously created using
3858 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
3860 * The @code attr@ parameter may be modified as a result of setting the value.
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@).
3868 * @since CUPS 1.6/macOS 10.8@
3871 int /* O - 1 on success, 0 on failure */
3873 ipp_t *ipp, /* I - IPP message */
3874 ipp_attribute_t **attr, /* IO - Attribute */
3875 ipp_tag_t group_tag) /* I - Group tag */
3878 * Range check input - group tag must be 0x01 to 0x0F, per RFC 8011...
3881 if (!ipp || !attr || !*attr ||
3882 group_tag < IPP_TAG_ZERO || group_tag == IPP_TAG_END ||
3883 group_tag >= IPP_TAG_UNSUPPORTED_VALUE)
3887 * Set the group tag and return...
3890 (*attr)->group_tag = group_tag;
3897 * 'ippSetInteger()' - Set an integer or enum value in an attribute.
3899 * The @code ipp@ parameter refers to an IPP message previously created using
3900 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
3902 * The @code attr@ parameter may be modified as a result of setting the value.
3904 * The @code element@ parameter specifies which value to set from 0 to
3905 * @code ippGetCount(attr)@.
3907 * @since CUPS 1.6/macOS 10.8@
3910 int /* O - 1 on success, 0 on failure */
3911 ippSetInteger(ipp_t *ipp, /* I - IPP message */
3912 ipp_attribute_t **attr, /* IO - IPP attribute */
3913 int element, /* I - Value number (0-based) */
3914 int intvalue) /* I - Integer/enum value */
3916 _ipp_value_t *value; /* Current value */
3920 * Range check input...
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)
3929 * Set the value and return...
3932 if ((value = ipp_set_value(ipp, attr, element)) != NULL)
3933 value->integer = intvalue;
3935 return (value != NULL);
3940 * 'ippSetName()' - Set the name of an attribute.
3942 * The @code ipp@ parameter refers to an IPP message previously created using
3943 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
3945 * The @code attr@ parameter may be modified as a result of setting the value.
3947 * @since CUPS 1.6/macOS 10.8@
3950 int /* O - 1 on success, 0 on failure */
3951 ippSetName(ipp_t *ipp, /* I - IPP message */
3952 ipp_attribute_t **attr, /* IO - IPP attribute */
3953 const char *name) /* I - Attribute name */
3955 char *temp; /* Temporary name value */
3959 * Range check input...
3962 if (!ipp || !attr || !*attr)
3966 * Set the value and return...
3969 if ((temp = _cupsStrAlloc(name)) != NULL)
3972 _cupsStrFree((*attr)->name);
3974 (*attr)->name = temp;
3977 return (temp != NULL);
3982 * 'ippSetOctetString()' - Set an octetString value in an IPP attribute.
3984 * The @code ipp@ parameter refers to an IPP message previously created using
3985 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
3987 * The @code attr@ parameter may be modified as a result of setting the value.
3989 * The @code element@ parameter specifies which value to set from 0 to
3990 * @code ippGetCount(attr)@.
3992 * @since CUPS 1.7/macOS 10.9@
3995 int /* O - 1 on success, 0 on failure */
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 */
4003 _ipp_value_t *value; /* Current value */
4007 * Range check input...
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)
4016 * Set the value and return...
4019 if ((value = ipp_set_value(ipp, attr, element)) != NULL)
4021 if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST)
4024 * Just copy the pointer...
4027 value->unknown.data = (void *)data;
4028 value->unknown.length = datalen;
4036 if (value->unknown.data)
4039 * Free previous data...
4042 free(value->unknown.data);
4044 value->unknown.data = NULL;
4045 value->unknown.length = 0;
4050 void *temp; /* Temporary data pointer */
4052 if ((temp = malloc((size_t)datalen)) != NULL)
4054 memcpy(temp, data, (size_t)datalen);
4056 value->unknown.data = temp;
4057 value->unknown.length = datalen;
4065 return (value != NULL);
4070 * 'ippSetOperation()' - Set the operation ID in an IPP request message.
4072 * The @code ipp@ parameter refers to an IPP message previously created using
4073 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
4075 * @since CUPS 1.6/macOS 10.8@
4078 int /* O - 1 on success, 0 on failure */
4079 ippSetOperation(ipp_t *ipp, /* I - IPP request message */
4080 ipp_op_t op) /* I - Operation ID */
4083 * Range check input...
4090 * Set the operation and return...
4093 ipp->request.op.operation_id = op;
4100 * 'ippSetRange()' - Set a rangeOfInteger value in an attribute.
4102 * The @code ipp@ parameter refers to an IPP message previously created using
4103 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
4105 * The @code attr@ parameter may be modified as a result of setting the value.
4107 * The @code element@ parameter specifies which value to set from 0 to
4108 * @code ippGetCount(attr)@.
4110 * @since CUPS 1.6/macOS 10.8@
4113 int /* O - 1 on success, 0 on failure */
4114 ippSetRange(ipp_t *ipp, /* I - IPP message */
4115 ipp_attribute_t **attr, /* IO - IPP attribute */
4116 int element, /* I - Value number (0-based) */
4117 int lowervalue, /* I - Lower bound for range */
4118 int uppervalue) /* I - Upper bound for range */
4120 _ipp_value_t *value; /* Current value */
4124 * Range check input...
4127 if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_RANGE ||
4128 element < 0 || element > (*attr)->num_values || lowervalue > uppervalue)
4132 * Set the value and return...
4135 if ((value = ipp_set_value(ipp, attr, element)) != NULL)
4137 value->range.lower = lowervalue;
4138 value->range.upper = uppervalue;
4141 return (value != NULL);
4146 * 'ippSetRequestId()' - Set the request ID in an IPP message.
4148 * The @code ipp@ parameter refers to an IPP message previously created using
4149 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
4151 * The @code request_id@ parameter must be greater than 0.
4153 * @since CUPS 1.6/macOS 10.8@
4156 int /* O - 1 on success, 0 on failure */
4157 ippSetRequestId(ipp_t *ipp, /* I - IPP message */
4158 int request_id) /* I - Request ID */
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...
4170 * Set the request ID and return...
4173 ipp->request.any.request_id = request_id;
4180 * 'ippSetResolution()' - Set a resolution value in an attribute.
4182 * The @code ipp@ parameter refers to an IPP message previously created using
4183 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
4185 * The @code attr@ parameter may be modified as a result of setting the value.
4187 * The @code element@ parameter specifies which value to set from 0 to
4188 * @code ippGetCount(attr)@.
4190 * @since CUPS 1.6/macOS 10.8@
4193 int /* O - 1 on success, 0 on failure */
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 */
4202 _ipp_value_t *value; /* Current value */
4206 * Range check input...
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)
4215 * Set the value and return...
4218 if ((value = ipp_set_value(ipp, attr, element)) != NULL)
4220 value->resolution.units = unitsvalue;
4221 value->resolution.xres = xresvalue;
4222 value->resolution.yres = yresvalue;
4225 return (value != NULL);
4230 * 'ippSetState()' - Set the current state of the IPP message.
4232 * @since CUPS 1.6/macOS 10.8@
4235 int /* O - 1 on success, 0 on failure */
4236 ippSetState(ipp_t *ipp, /* I - IPP message */
4237 ipp_state_t state) /* I - IPP state value */
4240 * Range check input...
4247 * Set the state and return...
4251 ipp->current = NULL;
4258 * 'ippSetStatusCode()' - Set the status code in an IPP response or event message.
4260 * The @code ipp@ parameter refers to an IPP message previously created using
4261 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
4263 * @since CUPS 1.6/macOS 10.8@
4266 int /* O - 1 on success, 0 on failure */
4267 ippSetStatusCode(ipp_t *ipp, /* I - IPP response or event message */
4268 ipp_status_t status) /* I - Status code */
4271 * Range check input...
4278 * Set the status code and return...
4281 ipp->request.status.status_code = status;
4288 * 'ippSetString()' - Set a string value in an attribute.
4290 * The @code ipp@ parameter refers to an IPP message previously created using
4291 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
4293 * The @code attr@ parameter may be modified as a result of setting the value.
4295 * The @code element@ parameter specifies which value to set from 0 to
4296 * @code ippGetCount(attr)@.
4298 * @since CUPS 1.6/macOS 10.8@
4301 int /* O - 1 on success, 0 on failure */
4302 ippSetString(ipp_t *ipp, /* I - IPP message */
4303 ipp_attribute_t **attr, /* IO - IPP attribute */
4304 int element, /* I - Value number (0-based) */
4305 const char *strvalue) /* I - String value */
4307 char *temp; /* Temporary string */
4308 _ipp_value_t *value; /* Current value */
4309 ipp_tag_t value_tag; /* Value tag */
4313 * Range check input...
4317 value_tag = (*attr)->value_tag & IPP_TAG_CUPS_MASK;
4319 value_tag = IPP_TAG_ZERO;
4321 if (!ipp || !attr || !*attr ||
4322 (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG &&
4323 value_tag != IPP_TAG_NAMELANG) || value_tag > IPP_TAG_MIMETYPE ||
4328 * Set the value and return...
4331 if ((value = ipp_set_value(ipp, attr, element)) != NULL)
4334 value->string.language = (*attr)->values[0].string.language;
4336 if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST)
4337 value->string.text = (char *)strvalue;
4338 else if ((temp = _cupsStrAlloc(strvalue)) != NULL)
4340 if (value->string.text)
4341 _cupsStrFree(value->string.text);
4343 value->string.text = temp;
4349 return (value != NULL);
4354 * 'ippSetStringf()' - Set a formatted string value of an attribute.
4356 * The @code ipp@ parameter refers to an IPP message previously created using
4357 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
4359 * The @code attr@ parameter may be modified as a result of setting the value.
4361 * The @code element@ parameter specifies which value to set from 0 to
4362 * @code ippGetCount(attr)@.
4364 * The @code format@ parameter uses formatting characters compatible with the
4365 * printf family of standard functions. Additional arguments follow it as
4366 * needed. The formatted string is truncated as needed to the maximum length of
4367 * the corresponding value type.
4369 * @since CUPS 1.7/macOS 10.9@
4372 int /* O - 1 on success, 0 on failure */
4373 ippSetStringf(ipp_t *ipp, /* I - IPP message */
4374 ipp_attribute_t **attr, /* IO - IPP attribute */
4375 int element, /* I - Value number (0-based) */
4376 const char *format, /* I - Printf-style format string */
4377 ...) /* I - Additional arguments as needed */
4379 int ret; /* Return value */
4380 va_list ap; /* Pointer to additional arguments */
4383 va_start(ap, format);
4384 ret = ippSetStringfv(ipp, attr, element, format, ap);
4392 * 'ippSetStringf()' - Set a formatted string value of an attribute.
4394 * The @code ipp@ parameter refers to an IPP message previously created using
4395 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
4397 * The @code attr@ parameter may be modified as a result of setting the value.
4399 * The @code element@ parameter specifies which value to set from 0 to
4400 * @code ippGetCount(attr)@.
4402 * The @code format@ parameter uses formatting characters compatible with the
4403 * printf family of standard functions. Additional arguments follow it as
4404 * needed. The formatted string is truncated as needed to the maximum length of
4405 * the corresponding value type.
4407 * @since CUPS 1.7/macOS 10.9@
4410 int /* O - 1 on success, 0 on failure */
4411 ippSetStringfv(ipp_t *ipp, /* I - IPP message */
4412 ipp_attribute_t **attr, /* IO - IPP attribute */
4413 int element, /* I - Value number (0-based) */
4414 const char *format, /* I - Printf-style format string */
4415 va_list ap) /* I - Pointer to additional arguments */
4417 ipp_tag_t value_tag; /* Value tag */
4418 char buffer[IPP_MAX_TEXT + 4];
4419 /* Formatted text string */
4420 ssize_t bytes, /* Length of formatted value */
4421 max_bytes; /* Maximum number of bytes for value */
4425 * Range check input...
4429 value_tag = (*attr)->value_tag & IPP_TAG_CUPS_MASK;
4431 value_tag = IPP_TAG_ZERO;
4433 if (!ipp || !attr || !*attr ||
4434 (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG &&
4435 value_tag != IPP_TAG_NAMELANG) || value_tag > IPP_TAG_MIMETYPE ||
4440 * Format the string...
4443 if (!strcmp(format, "%s"))
4446 * Optimize the simple case...
4449 const char *s = va_arg(ap, char *);
4454 bytes = (ssize_t)strlen(s);
4455 strlcpy(buffer, s, sizeof(buffer));
4460 * Do a full formatting of the message...
4463 if ((bytes = vsnprintf(buffer, sizeof(buffer), format, ap)) < 0)
4468 * Limit the length of the string...
4475 case IPP_TAG_TEXTLANG :
4476 max_bytes = IPP_MAX_TEXT;
4480 case IPP_TAG_NAMELANG :
4481 max_bytes = IPP_MAX_NAME;
4484 case IPP_TAG_CHARSET :
4485 max_bytes = IPP_MAX_CHARSET;
4488 case IPP_TAG_KEYWORD :
4489 max_bytes = IPP_MAX_KEYWORD;
4492 case IPP_TAG_LANGUAGE :
4493 max_bytes = IPP_MAX_LANGUAGE;
4496 case IPP_TAG_MIMETYPE :
4497 max_bytes = IPP_MAX_MIMETYPE;
4501 max_bytes = IPP_MAX_URI;
4504 case IPP_TAG_URISCHEME :
4505 max_bytes = IPP_MAX_URISCHEME;
4509 if (bytes >= max_bytes)
4511 char *bufmax, /* Buffer at max_bytes */
4512 *bufptr; /* Pointer into buffer */
4514 bufptr = buffer + strlen(buffer) - 1;
4515 bufmax = buffer + max_bytes - 1;
4517 while (bufptr > bufmax)
4521 while ((*bufptr & 0xc0) == 0x80 && bufptr > buffer)
4532 * Set the formatted string and return...
4535 return (ippSetString(ipp, attr, element, buffer));
4540 * 'ippSetValueTag()' - Set the value tag of an attribute.
4542 * The @code ipp@ parameter refers to an IPP message previously created using
4543 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
4545 * The @code attr@ parameter may be modified as a result of setting the value.
4547 * Integer (@code IPP_TAG_INTEGER@) values can be promoted to rangeOfInteger
4548 * (@code IPP_TAG_RANGE@) values, the various string tags can be promoted to name
4549 * (@code IPP_TAG_NAME@) or nameWithLanguage (@code IPP_TAG_NAMELANG@) values, text
4550 * (@code IPP_TAG_TEXT@) values can be promoted to textWithLanguage
4551 * (@code IPP_TAG_TEXTLANG@) values, and all values can be demoted to the various
4552 * out-of-band value tags such as no-value (@code IPP_TAG_NOVALUE@). All other changes
4555 * Promoting a string attribute to nameWithLanguage or textWithLanguage adds the language
4556 * code in the "attributes-natural-language" attribute or, if not present, the language
4557 * code for the current locale.
4559 * @since CUPS 1.6/macOS 10.8@
4562 int /* O - 1 on success, 0 on failure */
4564 ipp_t *ipp, /* I - IPP message */
4565 ipp_attribute_t **attr, /* IO - IPP attribute */
4566 ipp_tag_t value_tag) /* I - Value tag */
4568 int i; /* Looping var */
4569 _ipp_value_t *value; /* Current value */
4570 int integer; /* Current integer value */
4571 cups_lang_t *language; /* Current language */
4572 char code[32]; /* Language code */
4573 ipp_tag_t temp_tag; /* Temporary value tag */
4577 * Range check input...
4580 if (!ipp || !attr || !*attr)
4584 * If there is no change, return immediately...
4587 if (value_tag == (*attr)->value_tag)
4591 * Otherwise implement changes as needed...
4594 temp_tag = (ipp_tag_t)((int)((*attr)->value_tag) & IPP_TAG_CUPS_MASK);
4598 case IPP_TAG_UNSUPPORTED_VALUE :
4599 case IPP_TAG_DEFAULT :
4600 case IPP_TAG_UNKNOWN :
4601 case IPP_TAG_NOVALUE :
4602 case IPP_TAG_NOTSETTABLE :
4603 case IPP_TAG_DELETEATTR :
4604 case IPP_TAG_ADMINDEFINE :
4606 * Free any existing values...
4609 if ((*attr)->num_values > 0)
4610 ipp_free_values(*attr, 0, (*attr)->num_values);
4613 * Set out-of-band value...
4616 (*attr)->value_tag = value_tag;
4619 case IPP_TAG_RANGE :
4620 if (temp_tag != IPP_TAG_INTEGER)
4623 for (i = (*attr)->num_values, value = (*attr)->values;
4627 integer = value->integer;
4628 value->range.lower = value->range.upper = integer;
4631 (*attr)->value_tag = IPP_TAG_RANGE;
4635 if (temp_tag != IPP_TAG_KEYWORD && temp_tag != IPP_TAG_URI &&
4636 temp_tag != IPP_TAG_URISCHEME && temp_tag != IPP_TAG_LANGUAGE &&
4637 temp_tag != IPP_TAG_MIMETYPE)
4640 (*attr)->value_tag = (ipp_tag_t)(IPP_TAG_NAME | ((*attr)->value_tag & IPP_TAG_CUPS_CONST));
4643 case IPP_TAG_NAMELANG :
4644 case IPP_TAG_TEXTLANG :
4645 if (value_tag == IPP_TAG_NAMELANG &&
4646 (temp_tag != IPP_TAG_NAME && temp_tag != IPP_TAG_KEYWORD &&
4647 temp_tag != IPP_TAG_URI && temp_tag != IPP_TAG_URISCHEME &&
4648 temp_tag != IPP_TAG_LANGUAGE && temp_tag != IPP_TAG_MIMETYPE))
4651 if (value_tag == IPP_TAG_TEXTLANG && temp_tag != IPP_TAG_TEXT)
4654 if (ipp->attrs && ipp->attrs->next && ipp->attrs->next->name &&
4655 !strcmp(ipp->attrs->next->name, "attributes-natural-language"))
4658 * Use the language code from the IPP message...
4661 (*attr)->values[0].string.language =
4662 _cupsStrAlloc(ipp->attrs->next->values[0].string.text);
4667 * Otherwise, use the language code corresponding to the locale...
4670 language = cupsLangDefault();
4671 (*attr)->values[0].string.language = _cupsStrAlloc(ipp_lang_code(language->language,
4676 for (i = (*attr)->num_values - 1, value = (*attr)->values + 1;
4679 value->string.language = (*attr)->values[0].string.language;
4681 if ((int)(*attr)->value_tag & IPP_TAG_CUPS_CONST)
4684 * Make copies of all values...
4687 for (i = (*attr)->num_values, value = (*attr)->values;
4690 value->string.text = _cupsStrAlloc(value->string.text);
4693 (*attr)->value_tag = IPP_TAG_NAMELANG;
4696 case IPP_TAG_KEYWORD :
4697 if (temp_tag == IPP_TAG_NAME || temp_tag == IPP_TAG_NAMELANG)
4698 break; /* Silently "allow" name -> keyword */
4709 * 'ippSetVersion()' - Set the version number in an IPP message.
4711 * The @code ipp@ parameter refers to an IPP message previously created using
4712 * the @link ippNew@, @link ippNewRequest@, or @link ippNewResponse@ functions.
4714 * The valid version numbers are currently 1.0, 1.1, 2.0, 2.1, and 2.2.
4716 * @since CUPS 1.6/macOS 10.8@
4719 int /* O - 1 on success, 0 on failure */
4720 ippSetVersion(ipp_t *ipp, /* I - IPP message */
4721 int major, /* I - Major version number (major.minor) */
4722 int minor) /* I - Minor version number (major.minor) */
4725 * Range check input...
4728 if (!ipp || major < 0 || minor < 0)
4732 * Set the version number...
4735 ipp->request.any.version[0] = (ipp_uchar_t)major;
4736 ipp->request.any.version[1] = (ipp_uchar_t)minor;
4743 * 'ippTimeToDate()' - Convert from time in seconds to RFC 2579 format.
4746 const ipp_uchar_t * /* O - RFC-2579 date/time data */
4747 ippTimeToDate(time_t t) /* I - Time in seconds */
4749 struct tm *unixdate; /* UNIX unixdate/time info */
4750 ipp_uchar_t *date = _cupsGlobals()->ipp_date;
4751 /* RFC-2579 date/time data */
4755 * RFC-2579 date/time format is:
4757 * Byte(s) Description
4758 * ------- -----------
4759 * 0-1 Year (0 to 65535)
4763 * 5 Minutes (0 to 59)
4764 * 6 Seconds (0 to 60, 60 = "leap second")
4765 * 7 Deciseconds (0 to 9)
4767 * 9 UTC hours (0 to 11)
4768 * 10 UTC minutes (0 to 59)
4771 unixdate = gmtime(&t);
4772 unixdate->tm_year += 1900;
4774 date[0] = (ipp_uchar_t)(unixdate->tm_year >> 8);
4775 date[1] = (ipp_uchar_t)(unixdate->tm_year);
4776 date[2] = (ipp_uchar_t)(unixdate->tm_mon + 1);
4777 date[3] = (ipp_uchar_t)unixdate->tm_mday;
4778 date[4] = (ipp_uchar_t)unixdate->tm_hour;
4779 date[5] = (ipp_uchar_t)unixdate->tm_min;
4780 date[6] = (ipp_uchar_t)unixdate->tm_sec;
4791 * 'ippValidateAttribute()' - Validate the contents of an attribute.
4793 * This function validates the contents of an attribute based on the name and
4794 * value tag. 1 is returned if the attribute is valid, 0 otherwise. On
4795 * failure, @link cupsLastErrorString@ is set to a human-readable message.
4797 * @since CUPS 1.7/macOS 10.9@
4800 int /* O - 1 if valid, 0 otherwise */
4801 ippValidateAttribute(
4802 ipp_attribute_t *attr) /* I - Attribute */
4804 int i; /* Looping var */
4805 char scheme[64], /* Scheme from URI */
4806 userpass[256], /* Username/password from URI */
4807 hostname[256], /* Hostname from URI */
4808 resource[1024]; /* Resource from URI */
4809 int port, /* Port number from URI */
4810 uri_status; /* URI separation status */
4811 const char *ptr; /* Pointer into string */
4812 ipp_attribute_t *colattr; /* Collection attribute */
4813 regex_t re; /* Regular expression */
4814 ipp_uchar_t *date; /* Current date value */
4815 static const char * const uri_status_strings[] =
4816 { /* URI status strings */
4818 "Bad arguments to function",
4819 "Bad resource in URI",
4820 "Bad port number in URI",
4821 "Bad hostname/address in URI",
4822 "Bad username in URI",
4823 "Bad scheme in URI",
4826 "Missing scheme in URI",
4827 "Unknown scheme in URI",
4828 "Missing resource in URI"
4840 * Validate the attribute name.
4843 for (ptr = attr->name; *ptr; ptr ++)
4844 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_')
4847 if (*ptr || ptr == attr->name)
4849 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4850 _("\"%s\": Bad attribute name - invalid character "
4851 "(RFC 8011 section 5.1.4)."), attr->name);
4855 if ((ptr - attr->name) > 255)
4857 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4858 _("\"%s\": Bad attribute name - bad length %d "
4859 "(RFC 8011 section 5.1.4)."), attr->name,
4860 (int)(ptr - attr->name));
4864 switch (attr->value_tag)
4866 case IPP_TAG_INTEGER :
4869 case IPP_TAG_BOOLEAN :
4870 for (i = 0; i < attr->num_values; i ++)
4872 if (attr->values[i].boolean != 0 &&
4873 attr->values[i].boolean != 1)
4875 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4876 _("\"%s\": Bad boolen value %d "
4877 "(RFC 8011 section 5.1.21)."), attr->name,
4878 attr->values[i].boolean);
4885 for (i = 0; i < attr->num_values; i ++)
4887 if (attr->values[i].integer < 1)
4889 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4890 _("\"%s\": Bad enum value %d - out of range "
4891 "(RFC 8011 section 5.1.5)."), attr->name,
4892 attr->values[i].integer);
4898 case IPP_TAG_STRING :
4899 for (i = 0; i < attr->num_values; i ++)
4901 if (attr->values[i].unknown.length > IPP_MAX_OCTETSTRING)
4903 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4904 _("\"%s\": Bad octetString value - bad length %d "
4905 "(RFC 8011 section 5.1.20)."), attr->name,
4906 attr->values[i].unknown.length);
4913 for (i = 0; i < attr->num_values; i ++)
4915 date = attr->values[i].date;
4917 if (date[2] < 1 || date[2] > 12)
4919 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4920 _("\"%s\": Bad dateTime month %u "
4921 "(RFC 8011 section 5.1.15)."), attr->name, date[2]);
4925 if (date[3] < 1 || date[3] > 31)
4927 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4928 _("\"%s\": Bad dateTime day %u "
4929 "(RFC 8011 section 5.1.15)."), attr->name, date[3]);
4935 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4936 _("\"%s\": Bad dateTime hours %u "
4937 "(RFC 8011 section 5.1.15)."), attr->name, date[4]);
4943 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4944 _("\"%s\": Bad dateTime minutes %u "
4945 "(RFC 8011 section 5.1.15)."), attr->name, date[5]);
4951 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4952 _("\"%s\": Bad dateTime seconds %u "
4953 "(RFC 8011 section 5.1.15)."), attr->name, date[6]);
4959 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4960 _("\"%s\": Bad dateTime deciseconds %u "
4961 "(RFC 8011 section 5.1.15)."), attr->name, date[7]);
4965 if (date[8] != '-' && date[8] != '+')
4967 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4968 _("\"%s\": Bad dateTime UTC sign '%c' "
4969 "(RFC 8011 section 5.1.15)."), attr->name, date[8]);
4975 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4976 _("\"%s\": Bad dateTime UTC hours %u "
4977 "(RFC 8011 section 5.1.15)."), attr->name, date[9]);
4983 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4984 _("\"%s\": Bad dateTime UTC minutes %u "
4985 "(RFC 8011 section 5.1.15)."), attr->name, date[10]);
4991 case IPP_TAG_RESOLUTION :
4992 for (i = 0; i < attr->num_values; i ++)
4994 if (attr->values[i].resolution.xres <= 0)
4996 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
4997 _("\"%s\": Bad resolution value %dx%d%s - cross "
4998 "feed resolution must be positive "
4999 "(RFC 8011 section 5.1.16)."), attr->name,
5000 attr->values[i].resolution.xres,
5001 attr->values[i].resolution.yres,
5002 attr->values[i].resolution.units ==
5003 IPP_RES_PER_INCH ? "dpi" :
5004 attr->values[i].resolution.units ==
5005 IPP_RES_PER_CM ? "dpcm" : "unknown");
5009 if (attr->values[i].resolution.yres <= 0)
5011 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5012 _("\"%s\": Bad resolution value %dx%d%s - feed "
5013 "resolution must be positive "
5014 "(RFC 8011 section 5.1.16)."), attr->name,
5015 attr->values[i].resolution.xres,
5016 attr->values[i].resolution.yres,
5017 attr->values[i].resolution.units ==
5018 IPP_RES_PER_INCH ? "dpi" :
5019 attr->values[i].resolution.units ==
5020 IPP_RES_PER_CM ? "dpcm" : "unknown");
5024 if (attr->values[i].resolution.units != IPP_RES_PER_INCH &&
5025 attr->values[i].resolution.units != IPP_RES_PER_CM)
5027 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5028 _("\"%s\": Bad resolution value %dx%d%s - bad "
5029 "units value (RFC 8011 section 5.1.16)."),
5030 attr->name, attr->values[i].resolution.xres,
5031 attr->values[i].resolution.yres,
5032 attr->values[i].resolution.units ==
5033 IPP_RES_PER_INCH ? "dpi" :
5034 attr->values[i].resolution.units ==
5035 IPP_RES_PER_CM ? "dpcm" : "unknown");
5041 case IPP_TAG_RANGE :
5042 for (i = 0; i < attr->num_values; i ++)
5044 if (attr->values[i].range.lower > attr->values[i].range.upper)
5046 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5047 _("\"%s\": Bad rangeOfInteger value %d-%d - lower "
5048 "greater than upper (RFC 8011 section 5.1.14)."),
5049 attr->name, attr->values[i].range.lower,
5050 attr->values[i].range.upper);
5056 case IPP_TAG_BEGIN_COLLECTION :
5057 for (i = 0; i < attr->num_values; i ++)
5059 for (colattr = attr->values[i].collection->attrs;
5061 colattr = colattr->next)
5063 if (!ippValidateAttribute(colattr))
5070 case IPP_TAG_TEXTLANG :
5071 for (i = 0; i < attr->num_values; i ++)
5073 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5075 if ((*ptr & 0xe0) == 0xc0)
5078 if ((*ptr & 0xc0) != 0x80)
5081 else if ((*ptr & 0xf0) == 0xe0)
5084 if ((*ptr & 0xc0) != 0x80)
5087 if ((*ptr & 0xc0) != 0x80)
5090 else if ((*ptr & 0xf8) == 0xf0)
5093 if ((*ptr & 0xc0) != 0x80)
5096 if ((*ptr & 0xc0) != 0x80)
5099 if ((*ptr & 0xc0) != 0x80)
5102 else if (*ptr & 0x80)
5108 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5109 _("\"%s\": Bad text value \"%s\" - bad UTF-8 "
5110 "sequence (RFC 8011 section 5.1.2)."), attr->name,
5111 attr->values[i].string.text);
5115 if ((ptr - attr->values[i].string.text) > (IPP_MAX_TEXT - 1))
5117 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5118 _("\"%s\": Bad text value \"%s\" - bad length %d "
5119 "(RFC 8011 section 5.1.2)."), attr->name,
5120 attr->values[i].string.text,
5121 (int)(ptr - attr->values[i].string.text));
5128 case IPP_TAG_NAMELANG :
5129 for (i = 0; i < attr->num_values; i ++)
5131 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5133 if ((*ptr & 0xe0) == 0xc0)
5136 if ((*ptr & 0xc0) != 0x80)
5139 else if ((*ptr & 0xf0) == 0xe0)
5142 if ((*ptr & 0xc0) != 0x80)
5145 if ((*ptr & 0xc0) != 0x80)
5148 else if ((*ptr & 0xf8) == 0xf0)
5151 if ((*ptr & 0xc0) != 0x80)
5154 if ((*ptr & 0xc0) != 0x80)
5157 if ((*ptr & 0xc0) != 0x80)
5160 else if (*ptr & 0x80)
5166 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5167 _("\"%s\": Bad name value \"%s\" - bad UTF-8 "
5168 "sequence (RFC 8011 section 5.1.3)."), attr->name,
5169 attr->values[i].string.text);
5173 if ((ptr - attr->values[i].string.text) > (IPP_MAX_NAME - 1))
5175 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5176 _("\"%s\": Bad name value \"%s\" - bad length %d "
5177 "(RFC 8011 section 5.1.3)."), attr->name,
5178 attr->values[i].string.text,
5179 (int)(ptr - attr->values[i].string.text));
5185 case IPP_TAG_KEYWORD :
5186 for (i = 0; i < attr->num_values; i ++)
5188 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5189 if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' &&
5193 if (*ptr || ptr == attr->values[i].string.text)
5195 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5196 _("\"%s\": Bad keyword value \"%s\" - invalid "
5197 "character (RFC 8011 section 5.1.4)."),
5198 attr->name, attr->values[i].string.text);
5202 if ((ptr - attr->values[i].string.text) > (IPP_MAX_KEYWORD - 1))
5204 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5205 _("\"%s\": Bad keyword value \"%s\" - bad "
5206 "length %d (RFC 8011 section 5.1.4)."),
5207 attr->name, attr->values[i].string.text,
5208 (int)(ptr - attr->values[i].string.text));
5215 for (i = 0; i < attr->num_values; i ++)
5217 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
5218 attr->values[i].string.text,
5219 scheme, sizeof(scheme),
5220 userpass, sizeof(userpass),
5221 hostname, sizeof(hostname),
5222 &port, resource, sizeof(resource));
5224 if (uri_status < HTTP_URI_STATUS_OK)
5226 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5227 _("\"%s\": Bad URI value \"%s\" - %s "
5228 "(RFC 8011 section 5.1.6)."), attr->name,
5229 attr->values[i].string.text,
5230 uri_status_strings[uri_status -
5231 HTTP_URI_STATUS_OVERFLOW]);
5235 if (strlen(attr->values[i].string.text) > (IPP_MAX_URI - 1))
5237 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5238 _("\"%s\": Bad URI value \"%s\" - bad length %d "
5239 "(RFC 8011 section 5.1.6)."), attr->name,
5240 attr->values[i].string.text,
5241 (int)strlen(attr->values[i].string.text));
5246 case IPP_TAG_URISCHEME :
5247 for (i = 0; i < attr->num_values; i ++)
5249 ptr = attr->values[i].string.text;
5250 if (islower(*ptr & 255))
5252 for (ptr ++; *ptr; ptr ++)
5253 if (!islower(*ptr & 255) && !isdigit(*ptr & 255) &&
5254 *ptr != '+' && *ptr != '-' && *ptr != '.')
5258 if (*ptr || ptr == attr->values[i].string.text)
5260 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5261 _("\"%s\": Bad uriScheme value \"%s\" - bad "
5262 "characters (RFC 8011 section 5.1.7)."),
5263 attr->name, attr->values[i].string.text);
5267 if ((ptr - attr->values[i].string.text) > (IPP_MAX_URISCHEME - 1))
5269 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5270 _("\"%s\": Bad uriScheme value \"%s\" - bad "
5271 "length %d (RFC 8011 section 5.1.7)."),
5272 attr->name, attr->values[i].string.text,
5273 (int)(ptr - attr->values[i].string.text));
5279 case IPP_TAG_CHARSET :
5280 for (i = 0; i < attr->num_values; i ++)
5282 for (ptr = attr->values[i].string.text; *ptr; ptr ++)
5283 if (!isprint(*ptr & 255) || isupper(*ptr & 255) ||
5284 isspace(*ptr & 255))
5287 if (*ptr || ptr == attr->values[i].string.text)
5289 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5290 _("\"%s\": Bad charset value \"%s\" - bad "
5291 "characters (RFC 8011 section 5.1.8)."),
5292 attr->name, attr->values[i].string.text);
5296 if ((ptr - attr->values[i].string.text) > (IPP_MAX_CHARSET - 1))
5298 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5299 _("\"%s\": Bad charset value \"%s\" - bad "
5300 "length %d (RFC 8011 section 5.1.8)."),
5301 attr->name, attr->values[i].string.text,
5302 (int)(ptr - attr->values[i].string.text));
5308 case IPP_TAG_LANGUAGE :
5310 * The following regular expression is derived from the ABNF for
5311 * language tags in RFC 4646. All I can say is that this is the
5312 * easiest way to check the values...
5315 if ((i = regcomp(&re,
5317 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
5319 "(-[a-z][a-z][a-z][a-z]){0,1}" /* script */
5320 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}" /* region */
5321 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*" /* variant */
5322 "(-[a-wy-z](-[a-z0-9]{2,8})+)*" /* extension */
5323 "(-x(-[a-z0-9]{1,8})+)*" /* privateuse */
5325 "x(-[a-z0-9]{1,8})+" /* privateuse */
5327 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}" /* grandfathered */
5329 REG_NOSUB | REG_EXTENDED)) != 0)
5331 char temp[256]; /* Temporary error string */
5333 regerror(i, &re, temp, sizeof(temp));
5334 ipp_set_error(IPP_STATUS_ERROR_INTERNAL,
5335 _("Unable to compile naturalLanguage regular "
5336 "expression: %s."), temp);
5340 for (i = 0; i < attr->num_values; i ++)
5342 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
5344 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5345 _("\"%s\": Bad naturalLanguage value \"%s\" - bad "
5346 "characters (RFC 8011 section 5.1.9)."),
5347 attr->name, attr->values[i].string.text);
5352 if (strlen(attr->values[i].string.text) > (IPP_MAX_LANGUAGE - 1))
5354 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5355 _("\"%s\": Bad naturalLanguage value \"%s\" - bad "
5356 "length %d (RFC 8011 section 5.1.9)."),
5357 attr->name, attr->values[i].string.text,
5358 (int)strlen(attr->values[i].string.text));
5367 case IPP_TAG_MIMETYPE :
5369 * The following regular expression is derived from the ABNF for
5370 * MIME media types in RFC 2045 and 4288. All I can say is that this is
5371 * the easiest way to check the values...
5374 if ((i = regcomp(&re,
5376 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* type-name */
5378 "[-a-zA-Z0-9!#$&.+^_]{1,127}" /* subtype-name */
5379 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}=" /* parameter= */
5380 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
5383 REG_NOSUB | REG_EXTENDED)) != 0)
5385 char temp[256]; /* Temporary error string */
5387 regerror(i, &re, temp, sizeof(temp));
5388 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5389 _("Unable to compile mimeMediaType regular "
5390 "expression: %s."), temp);
5394 for (i = 0; i < attr->num_values; i ++)
5396 if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
5398 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5399 _("\"%s\": Bad mimeMediaType value \"%s\" - bad "
5400 "characters (RFC 8011 section 5.1.10)."),
5401 attr->name, attr->values[i].string.text);
5406 if (strlen(attr->values[i].string.text) > (IPP_MAX_MIMETYPE - 1))
5408 ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
5409 _("\"%s\": Bad mimeMediaType value \"%s\" - bad "
5410 "length %d (RFC 8011 section 5.1.10)."),
5411 attr->name, attr->values[i].string.text,
5412 (int)strlen(attr->values[i].string.text));
5430 * 'ippValidateAttributes()' - Validate all attributes in an IPP message.
5432 * This function validates the contents of the IPP message, including each
5433 * attribute. Like @link ippValidateAttribute@, @link cupsLastErrorString@ is
5434 * set to a human-readable message on failure.
5436 * @since CUPS 1.7/macOS 10.9@
5439 int /* O - 1 if valid, 0 otherwise */
5440 ippValidateAttributes(ipp_t *ipp) /* I - IPP message */
5442 ipp_attribute_t *attr; /* Current attribute */
5448 for (attr = ipp->attrs; attr; attr = attr->next)
5449 if (!ippValidateAttribute(attr))
5457 * 'ippWrite()' - Write data for an IPP message to a HTTP connection.
5460 ipp_state_t /* O - Current state */
5461 ippWrite(http_t *http, /* I - HTTP connection */
5462 ipp_t *ipp) /* I - IPP data */
5464 DEBUG_printf(("ippWrite(http=%p, ipp=%p)", (void *)http, (void *)ipp));
5467 return (IPP_STATE_ERROR);
5469 return (ippWriteIO(http, (ipp_iocb_t)httpWrite2, http->blocking, NULL, ipp));
5474 * 'ippWriteFile()' - Write data for an IPP message to a file.
5476 * @since CUPS 1.1.19/macOS 10.3@
5479 ipp_state_t /* O - Current state */
5480 ippWriteFile(int fd, /* I - HTTP data */
5481 ipp_t *ipp) /* I - IPP data */
5483 DEBUG_printf(("ippWriteFile(fd=%d, ipp=%p)", fd, (void *)ipp));
5485 ipp->state = IPP_STATE_IDLE;
5487 return (ippWriteIO(&fd, (ipp_iocb_t)ipp_write_file, 1, NULL, ipp));
5492 * 'ippWriteIO()' - Write data for an IPP message.
5494 * @since CUPS 1.2/macOS 10.5@
5497 ipp_state_t /* O - Current state */
5498 ippWriteIO(void *dst, /* I - Destination */
5499 ipp_iocb_t cb, /* I - Write callback function */
5500 int blocking, /* I - Use blocking IO? */
5501 ipp_t *parent, /* I - Parent IPP message */
5502 ipp_t *ipp) /* I - IPP data */
5504 int i; /* Looping var */
5505 int n; /* Length of data */
5506 unsigned char *buffer, /* Data buffer */
5507 *bufptr; /* Pointer into buffer */
5508 ipp_attribute_t *attr; /* Current attribute */
5509 _ipp_value_t *value; /* Current value */
5512 DEBUG_printf(("ippWriteIO(dst=%p, cb=%p, blocking=%d, parent=%p, ipp=%p)", (void *)dst, (void *)cb, blocking, (void *)parent, (void *)ipp));
5515 return (IPP_STATE_ERROR);
5517 if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL)
5519 DEBUG_puts("1ippWriteIO: Unable to get write buffer");
5520 return (IPP_STATE_ERROR);
5525 case IPP_STATE_IDLE :
5526 ipp->state ++; /* Avoid common problem... */
5528 case IPP_STATE_HEADER :
5532 * Send the request header:
5535 * Operation/Status Code = 2 bytes
5536 * Request ID = 4 bytes
5542 *bufptr++ = ipp->request.any.version[0];
5543 *bufptr++ = ipp->request.any.version[1];
5544 *bufptr++ = (ipp_uchar_t)(ipp->request.any.op_status >> 8);
5545 *bufptr++ = (ipp_uchar_t)ipp->request.any.op_status;
5546 *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 24);
5547 *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 16);
5548 *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 8);
5549 *bufptr++ = (ipp_uchar_t)ipp->request.any.request_id;
5551 DEBUG_printf(("2ippWriteIO: version=%d.%d", buffer[0], buffer[1]));
5552 DEBUG_printf(("2ippWriteIO: op_status=%04x",
5553 ipp->request.any.op_status));
5554 DEBUG_printf(("2ippWriteIO: request_id=%d",
5555 ipp->request.any.request_id));
5557 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5559 DEBUG_puts("1ippWriteIO: Could not write IPP header...");
5560 _cupsBufferRelease((char *)buffer);
5561 return (IPP_STATE_ERROR);
5566 * Reset the state engine to point to the first attribute
5567 * in the request/response, with no current group.
5570 ipp->state = IPP_STATE_ATTRIBUTE;
5571 ipp->current = ipp->attrs;
5572 ipp->curtag = IPP_TAG_ZERO;
5574 DEBUG_printf(("1ippWriteIO: ipp->current=%p", (void *)ipp->current));
5577 * If blocking is disabled, stop here...
5583 case IPP_STATE_ATTRIBUTE :
5584 while (ipp->current != NULL)
5587 * Write this attribute...
5591 attr = ipp->current;
5593 ipp->current = ipp->current->next;
5597 if (ipp->curtag != attr->group_tag)
5600 * Send a group tag byte...
5603 ipp->curtag = attr->group_tag;
5605 if (attr->group_tag == IPP_TAG_ZERO)
5608 DEBUG_printf(("2ippWriteIO: wrote group tag=%x(%s)",
5609 attr->group_tag, ippTagString(attr->group_tag)));
5610 *bufptr++ = (ipp_uchar_t)attr->group_tag;
5612 else if (attr->group_tag == IPP_TAG_ZERO)
5616 DEBUG_printf(("1ippWriteIO: %s (%s%s)", attr->name,
5617 attr->num_values > 1 ? "1setOf " : "",
5618 ippTagString(attr->value_tag)));
5621 * Write the attribute tag and name.
5623 * The attribute name length does not include the trailing nul
5624 * character in the source string.
5626 * Collection values (parent != NULL) are written differently...
5632 * Get the length of the attribute name, and make sure it won't
5633 * overflow the buffer...
5636 if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 8))
5638 DEBUG_printf(("1ippWriteIO: Attribute name too long (%d)", n));
5639 _cupsBufferRelease((char *)buffer);
5640 return (IPP_STATE_ERROR);
5644 * Write the value tag, name length, and name string...
5647 DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
5648 attr->value_tag, ippTagString(attr->value_tag)));
5649 DEBUG_printf(("2ippWriteIO: writing name=%d,\"%s\"", n,
5652 if (attr->value_tag > 0xff)
5654 *bufptr++ = IPP_TAG_EXTENSION;
5655 *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 24);
5656 *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 16);
5657 *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 8);
5658 *bufptr++ = (ipp_uchar_t)attr->value_tag;
5661 *bufptr++ = (ipp_uchar_t)attr->value_tag;
5663 *bufptr++ = (ipp_uchar_t)(n >> 8);
5664 *bufptr++ = (ipp_uchar_t)n;
5665 memcpy(bufptr, attr->name, (size_t)n);
5671 * Get the length of the attribute name, and make sure it won't
5672 * overflow the buffer...
5675 if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 12))
5677 DEBUG_printf(("1ippWriteIO: Attribute name too long (%d)", n));
5678 _cupsBufferRelease((char *)buffer);
5679 return (IPP_STATE_ERROR);
5683 * Write the member name tag, name length, name string, value tag,
5684 * and empty name for the collection member attribute...
5687 DEBUG_printf(("2ippWriteIO: writing value tag=%x(memberName)",
5688 IPP_TAG_MEMBERNAME));
5689 DEBUG_printf(("2ippWriteIO: writing name=%d,\"%s\"", n,
5691 DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
5692 attr->value_tag, ippTagString(attr->value_tag)));
5693 DEBUG_puts("2ippWriteIO: writing name=0,\"\"");
5695 *bufptr++ = IPP_TAG_MEMBERNAME;
5698 *bufptr++ = (ipp_uchar_t)(n >> 8);
5699 *bufptr++ = (ipp_uchar_t)n;
5700 memcpy(bufptr, attr->name, (size_t)n);
5703 if (attr->value_tag > 0xff)
5705 *bufptr++ = IPP_TAG_EXTENSION;
5706 *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 24);
5707 *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 16);
5708 *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 8);
5709 *bufptr++ = (ipp_uchar_t)attr->value_tag;
5712 *bufptr++ = (ipp_uchar_t)attr->value_tag;
5719 * Now write the attribute value(s)...
5722 switch (attr->value_tag & ~IPP_TAG_CUPS_CONST)
5724 case IPP_TAG_UNSUPPORTED_VALUE :
5725 case IPP_TAG_DEFAULT :
5726 case IPP_TAG_UNKNOWN :
5727 case IPP_TAG_NOVALUE :
5728 case IPP_TAG_NOTSETTABLE :
5729 case IPP_TAG_DELETEATTR :
5730 case IPP_TAG_ADMINDEFINE :
5735 case IPP_TAG_INTEGER :
5737 for (i = 0, value = attr->values;
5738 i < attr->num_values;
5741 if ((IPP_BUF_SIZE - (bufptr - buffer)) < 9)
5743 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5745 DEBUG_puts("1ippWriteIO: Could not write IPP "
5747 _cupsBufferRelease((char *)buffer);
5748 return (IPP_STATE_ERROR);
5757 * Arrays and sets are done by sending additional
5758 * values with a zero-length name...
5761 *bufptr++ = (ipp_uchar_t)attr->value_tag;
5767 * Integers and enumerations are both 4-byte signed
5768 * (twos-complement) values.
5770 * Put the 2-byte length and 4-byte value into the buffer...
5775 *bufptr++ = (ipp_uchar_t)(value->integer >> 24);
5776 *bufptr++ = (ipp_uchar_t)(value->integer >> 16);
5777 *bufptr++ = (ipp_uchar_t)(value->integer >> 8);
5778 *bufptr++ = (ipp_uchar_t)value->integer;
5782 case IPP_TAG_BOOLEAN :
5783 for (i = 0, value = attr->values;
5784 i < attr->num_values;
5787 if ((IPP_BUF_SIZE - (bufptr - buffer)) < 6)
5789 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5791 DEBUG_puts("1ippWriteIO: Could not write IPP "
5793 _cupsBufferRelease((char *)buffer);
5794 return (IPP_STATE_ERROR);
5803 * Arrays and sets are done by sending additional
5804 * values with a zero-length name...
5807 *bufptr++ = (ipp_uchar_t)attr->value_tag;
5813 * Boolean values are 1-byte; 0 = false, 1 = true.
5815 * Put the 2-byte length and 1-byte value into the buffer...
5820 *bufptr++ = (ipp_uchar_t)value->boolean;
5826 case IPP_TAG_KEYWORD :
5828 case IPP_TAG_URISCHEME :
5829 case IPP_TAG_CHARSET :
5830 case IPP_TAG_LANGUAGE :
5831 case IPP_TAG_MIMETYPE :
5832 for (i = 0, value = attr->values;
5833 i < attr->num_values;
5839 * Arrays and sets are done by sending additional
5840 * values with a zero-length name...
5843 DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
5845 ippTagString(attr->value_tag)));
5846 DEBUG_printf(("2ippWriteIO: writing name=0,\"\""));
5848 if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
5850 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5852 DEBUG_puts("1ippWriteIO: Could not write IPP "
5854 _cupsBufferRelease((char *)buffer);
5855 return (IPP_STATE_ERROR);
5861 *bufptr++ = (ipp_uchar_t)attr->value_tag;
5866 if (value->string.text != NULL)
5867 n = (int)strlen(value->string.text);
5871 if (n > (IPP_BUF_SIZE - 2))
5873 DEBUG_printf(("1ippWriteIO: String too long (%d)", n));
5874 _cupsBufferRelease((char *)buffer);
5875 return (IPP_STATE_ERROR);
5878 DEBUG_printf(("2ippWriteIO: writing string=%d,\"%s\"", n,
5879 value->string.text));
5881 if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
5883 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5885 DEBUG_puts("1ippWriteIO: Could not write IPP "
5887 _cupsBufferRelease((char *)buffer);
5888 return (IPP_STATE_ERROR);
5895 * All simple strings consist of the 2-byte length and
5896 * character data without the trailing nul normally found
5897 * in C strings. Also, strings cannot be longer than IPP_MAX_LENGTH
5898 * bytes since the 2-byte length is a signed (twos-complement)
5901 * Put the 2-byte length and string characters in the buffer.
5904 *bufptr++ = (ipp_uchar_t)(n >> 8);
5905 *bufptr++ = (ipp_uchar_t)n;
5909 memcpy(bufptr, value->string.text, (size_t)n);
5916 for (i = 0, value = attr->values;
5917 i < attr->num_values;
5920 if ((IPP_BUF_SIZE - (bufptr - buffer)) < 16)
5922 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5924 DEBUG_puts("1ippWriteIO: Could not write IPP "
5926 _cupsBufferRelease((char *)buffer);
5927 return (IPP_STATE_ERROR);
5936 * Arrays and sets are done by sending additional
5937 * values with a zero-length name...
5940 *bufptr++ = (ipp_uchar_t)attr->value_tag;
5946 * Date values consist of a 2-byte length and an
5947 * 11-byte date/time structure defined by RFC 1903.
5949 * Put the 2-byte length and 11-byte date/time
5950 * structure in the buffer.
5955 memcpy(bufptr, value->date, 11);
5960 case IPP_TAG_RESOLUTION :
5961 for (i = 0, value = attr->values;
5962 i < attr->num_values;
5965 if ((IPP_BUF_SIZE - (bufptr - buffer)) < 14)
5967 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5969 DEBUG_puts("1ippWriteIO: Could not write IPP "
5971 _cupsBufferRelease((char *)buffer);
5972 return (IPP_STATE_ERROR);
5981 * Arrays and sets are done by sending additional
5982 * values with a zero-length name...
5985 *bufptr++ = (ipp_uchar_t)attr->value_tag;
5991 * Resolution values consist of a 2-byte length,
5992 * 4-byte horizontal resolution value, 4-byte vertical
5993 * resolution value, and a 1-byte units value.
5995 * Put the 2-byte length and resolution value data
6001 *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 24);
6002 *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 16);
6003 *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 8);
6004 *bufptr++ = (ipp_uchar_t)value->resolution.xres;
6005 *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 24);
6006 *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 16);
6007 *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 8);
6008 *bufptr++ = (ipp_uchar_t)value->resolution.yres;
6009 *bufptr++ = (ipp_uchar_t)value->resolution.units;
6013 case IPP_TAG_RANGE :
6014 for (i = 0, value = attr->values;
6015 i < attr->num_values;
6018 if ((IPP_BUF_SIZE - (bufptr - buffer)) < 13)
6020 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
6022 DEBUG_puts("1ippWriteIO: Could not write IPP "
6024 _cupsBufferRelease((char *)buffer);
6025 return (IPP_STATE_ERROR);
6034 * Arrays and sets are done by sending additional
6035 * values with a zero-length name...
6038 *bufptr++ = (ipp_uchar_t)attr->value_tag;
6044 * Range values consist of a 2-byte length,
6045 * 4-byte lower value, and 4-byte upper value.
6047 * Put the 2-byte length and range value data
6053 *bufptr++ = (ipp_uchar_t)(value->range.lower >> 24);
6054 *bufptr++ = (ipp_uchar_t)(value->range.lower >> 16);
6055 *bufptr++ = (ipp_uchar_t)(value->range.lower >> 8);
6056 *bufptr++ = (ipp_uchar_t)value->range.lower;
6057 *bufptr++ = (ipp_uchar_t)(value->range.upper >> 24);
6058 *bufptr++ = (ipp_uchar_t)(value->range.upper >> 16);
6059 *bufptr++ = (ipp_uchar_t)(value->range.upper >> 8);
6060 *bufptr++ = (ipp_uchar_t)value->range.upper;
6064 case IPP_TAG_TEXTLANG :
6065 case IPP_TAG_NAMELANG :
6066 for (i = 0, value = attr->values;
6067 i < attr->num_values;
6073 * Arrays and sets are done by sending additional
6074 * values with a zero-length name...
6077 if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
6079 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
6081 DEBUG_puts("1ippWriteIO: Could not write IPP "
6083 _cupsBufferRelease((char *)buffer);
6084 return (IPP_STATE_ERROR);
6090 *bufptr++ = (ipp_uchar_t)attr->value_tag;
6096 * textWithLanguage and nameWithLanguage values consist
6097 * of a 2-byte length for both strings and their
6098 * individual lengths, a 2-byte length for the
6099 * character string, the character string without the
6100 * trailing nul, a 2-byte length for the character
6101 * set string, and the character set string without
6107 if (value->string.language != NULL)
6108 n += (int)strlen(value->string.language);
6110 if (value->string.text != NULL)
6111 n += (int)strlen(value->string.text);
6113 if (n > (IPP_BUF_SIZE - 2))
6115 DEBUG_printf(("1ippWriteIO: text/nameWithLanguage value "
6116 "too long (%d)", n));
6117 _cupsBufferRelease((char *)buffer);
6118 return (IPP_STATE_ERROR);
6121 if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
6123 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
6125 DEBUG_puts("1ippWriteIO: Could not write IPP "
6127 _cupsBufferRelease((char *)buffer);
6128 return (IPP_STATE_ERROR);
6134 /* Length of entire value */
6135 *bufptr++ = (ipp_uchar_t)(n >> 8);
6136 *bufptr++ = (ipp_uchar_t)n;
6138 /* Length of language */
6139 if (value->string.language != NULL)
6140 n = (int)strlen(value->string.language);
6144 *bufptr++ = (ipp_uchar_t)(n >> 8);
6145 *bufptr++ = (ipp_uchar_t)n;
6150 memcpy(bufptr, value->string.language, (size_t)n);
6154 /* Length of text */
6155 if (value->string.text != NULL)
6156 n = (int)strlen(value->string.text);
6160 *bufptr++ = (ipp_uchar_t)(n >> 8);
6161 *bufptr++ = (ipp_uchar_t)n;
6166 memcpy(bufptr, value->string.text, (size_t)n);
6172 case IPP_TAG_BEGIN_COLLECTION :
6173 for (i = 0, value = attr->values;
6174 i < attr->num_values;
6178 * Collections are written with the begin-collection
6179 * tag first with a value of 0 length, followed by the
6180 * attributes in the collection, then the end-collection
6184 if ((IPP_BUF_SIZE - (bufptr - buffer)) < 5)
6186 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
6188 DEBUG_puts("1ippWriteIO: Could not write IPP "
6190 _cupsBufferRelease((char *)buffer);
6191 return (IPP_STATE_ERROR);
6200 * Arrays and sets are done by sending additional
6201 * values with a zero-length name...
6204 *bufptr++ = (ipp_uchar_t)attr->value_tag;
6210 * Write a data length of 0 and flush the buffer...
6216 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
6218 DEBUG_puts("1ippWriteIO: Could not write IPP "
6220 _cupsBufferRelease((char *)buffer);
6221 return (IPP_STATE_ERROR);
6227 * Then write the collection attribute...
6230 value->collection->state = IPP_STATE_IDLE;
6232 if (ippWriteIO(dst, cb, 1, ipp,
6233 value->collection) == IPP_STATE_ERROR)
6235 DEBUG_puts("1ippWriteIO: Unable to write collection value");
6236 _cupsBufferRelease((char *)buffer);
6237 return (IPP_STATE_ERROR);
6243 for (i = 0, value = attr->values;
6244 i < attr->num_values;
6250 * Arrays and sets are done by sending additional
6251 * values with a zero-length name...
6254 if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
6256 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
6258 DEBUG_puts("1ippWriteIO: Could not write IPP "
6260 _cupsBufferRelease((char *)buffer);
6261 return (IPP_STATE_ERROR);
6267 *bufptr++ = (ipp_uchar_t)attr->value_tag;
6273 * An unknown value might some new value that a
6274 * vendor has come up with. It consists of a
6275 * 2-byte length and the bytes in the unknown
6279 n = value->unknown.length;
6281 if (n > (IPP_BUF_SIZE - 2))
6283 DEBUG_printf(("1ippWriteIO: Data length too long (%d)",
6285 _cupsBufferRelease((char *)buffer);
6286 return (IPP_STATE_ERROR);
6289 if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
6291 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
6293 DEBUG_puts("1ippWriteIO: Could not write IPP "
6295 _cupsBufferRelease((char *)buffer);
6296 return (IPP_STATE_ERROR);
6302 /* Length of unknown value */
6303 *bufptr++ = (ipp_uchar_t)(n >> 8);
6304 *bufptr++ = (ipp_uchar_t)n;
6309 memcpy(bufptr, value->unknown.data, (size_t)n);
6317 * Write the data out...
6320 if (bufptr > buffer)
6322 if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
6324 DEBUG_puts("1ippWriteIO: Could not write IPP attribute...");
6325 _cupsBufferRelease((char *)buffer);
6326 return (IPP_STATE_ERROR);
6329 DEBUG_printf(("2ippWriteIO: wrote %d bytes",
6330 (int)(bufptr - buffer)));
6334 * If blocking is disabled and we aren't at the end of the attribute
6335 * list, stop here...
6338 if (!blocking && ipp->current)
6342 if (ipp->current == NULL)
6345 * Done with all of the attributes; add the end-of-attributes
6346 * tag or end-collection attribute...
6351 buffer[0] = IPP_TAG_END;
6356 buffer[0] = IPP_TAG_END_COLLECTION;
6357 buffer[1] = 0; /* empty name */
6359 buffer[3] = 0; /* empty value */
6364 if ((*cb)(dst, buffer, (size_t)n) < 0)
6366 DEBUG_puts("1ippWriteIO: Could not write IPP end-tag...");
6367 _cupsBufferRelease((char *)buffer);
6368 return (IPP_STATE_ERROR);
6371 ipp->state = IPP_STATE_DATA;
6375 case IPP_STATE_DATA :
6379 break; /* anti-compiler-warning-code */
6382 _cupsBufferRelease((char *)buffer);
6384 return (ipp->state);
6389 * 'ipp_add_attr()' - Add a new attribute to the message.
6392 static ipp_attribute_t * /* O - New attribute */
6393 ipp_add_attr(ipp_t *ipp, /* I - IPP message */
6394 const char *name, /* I - Attribute name or NULL */
6395 ipp_tag_t group_tag, /* I - Group tag or IPP_TAG_ZERO */
6396 ipp_tag_t value_tag, /* I - Value tag or IPP_TAG_ZERO */
6397 int num_values) /* I - Number of values */
6399 int alloc_values; /* Number of values to allocate */
6400 ipp_attribute_t *attr; /* New attribute */
6403 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));
6406 * Range check input...
6409 if (!ipp || num_values < 0)
6413 * Allocate memory, rounding the allocation up as needed...
6416 if (num_values <= 1)
6419 alloc_values = (num_values + IPP_MAX_VALUES - 1) & ~(IPP_MAX_VALUES - 1);
6421 attr = calloc(sizeof(ipp_attribute_t) +
6422 (size_t)(alloc_values - 1) * sizeof(_ipp_value_t), 1);
6427 * Initialize attribute...
6430 DEBUG_printf(("4debug_alloc: %p %s %s%s (%d values)", (void *)attr, name, num_values > 1 ? "1setOf " : "", ippTagString(value_tag), num_values));
6433 attr->name = _cupsStrAlloc(name);
6435 attr->group_tag = group_tag;
6436 attr->value_tag = value_tag;
6437 attr->num_values = num_values;
6440 * Add it to the end of the linked list...
6444 ipp->last->next = attr;
6448 ipp->prev = ipp->last;
6449 ipp->last = ipp->current = attr;
6452 DEBUG_printf(("5ipp_add_attr: Returning %p", (void *)attr));
6459 * 'ipp_free_values()' - Free attribute values.
6463 ipp_free_values(ipp_attribute_t *attr, /* I - Attribute to free values from */
6464 int element,/* I - First value to free */
6465 int count) /* I - Number of values to free */
6467 int i; /* Looping var */
6468 _ipp_value_t *value; /* Current value */
6471 DEBUG_printf(("4ipp_free_values(attr=%p, element=%d, count=%d)", (void *)attr, element, count));
6473 if (!(attr->value_tag & IPP_TAG_CUPS_CONST))
6476 * Free values as needed...
6479 switch (attr->value_tag)
6481 case IPP_TAG_TEXTLANG :
6482 case IPP_TAG_NAMELANG :
6483 if (element == 0 && count == attr->num_values &&
6484 attr->values[0].string.language)
6486 _cupsStrFree(attr->values[0].string.language);
6487 attr->values[0].string.language = NULL;
6489 /* Fall through to other string values */
6493 case IPP_TAG_RESERVED_STRING :
6494 case IPP_TAG_KEYWORD :
6496 case IPP_TAG_URISCHEME :
6497 case IPP_TAG_CHARSET :
6498 case IPP_TAG_LANGUAGE :
6499 case IPP_TAG_MIMETYPE :
6500 for (i = count, value = attr->values + element;
6504 _cupsStrFree(value->string.text);
6505 value->string.text = NULL;
6509 case IPP_TAG_DEFAULT :
6510 case IPP_TAG_UNKNOWN :
6511 case IPP_TAG_NOVALUE :
6512 case IPP_TAG_NOTSETTABLE :
6513 case IPP_TAG_DELETEATTR :
6514 case IPP_TAG_ADMINDEFINE :
6515 case IPP_TAG_INTEGER :
6517 case IPP_TAG_BOOLEAN :
6519 case IPP_TAG_RESOLUTION :
6520 case IPP_TAG_RANGE :
6523 case IPP_TAG_BEGIN_COLLECTION :
6524 for (i = count, value = attr->values + element;
6528 ippDelete(value->collection);
6529 value->collection = NULL;
6533 case IPP_TAG_STRING :
6535 for (i = count, value = attr->values + element;
6539 if (value->unknown.data)
6541 free(value->unknown.data);
6542 value->unknown.data = NULL;
6550 * If we are not freeing values from the end, move the remaining values up...
6553 if ((element + count) < attr->num_values)
6554 memmove(attr->values + element, attr->values + element + count,
6555 (size_t)(attr->num_values - count - element) * sizeof(_ipp_value_t));
6557 attr->num_values -= count;
6562 * 'ipp_get_code()' - Convert a C locale/charset name into an IPP language/charset code.
6564 * This typically converts strings of the form "ll_CC", "ll-REGION", and "CHARSET_NUMBER"
6565 * to "ll-cc", "ll-region", and "charset-number", respectively.
6568 static char * /* O - Language code string */
6569 ipp_get_code(const char *value, /* I - Locale/charset string */
6570 char *buffer, /* I - String buffer */
6571 size_t bufsize) /* I - Size of string buffer */
6573 char *bufptr, /* Pointer into buffer */
6574 *bufend; /* End of buffer */
6578 * Convert values to lowercase and change _ to - as needed...
6581 for (bufptr = buffer, bufend = buffer + bufsize - 1;
6582 *value && bufptr < bufend;
6587 *bufptr++ = (char)_cups_tolower(*value);
6592 * Return the converted string...
6600 * 'ipp_lang_code()' - Convert a C locale name into an IPP language code.
6602 * This typically converts strings of the form "ll_CC" and "ll-REGION" to "ll-cc" and
6603 * "ll-region", respectively. It also converts the "C" (POSIX) locale to "en".
6606 static char * /* O - Language code string */
6607 ipp_lang_code(const char *locale, /* I - Locale string */
6608 char *buffer, /* I - String buffer */
6609 size_t bufsize) /* I - Size of string buffer */
6612 * Map POSIX ("C") locale to generic English, otherwise convert the locale string as-is.
6615 if (!_cups_strcasecmp(locale, "c"))
6617 strlcpy(buffer, "en", bufsize);
6621 return (ipp_get_code(locale, buffer, bufsize));
6626 * 'ipp_length()' - Compute the length of an IPP message or collection value.
6629 static size_t /* O - Size of IPP message */
6630 ipp_length(ipp_t *ipp, /* I - IPP message or collection */
6631 int collection) /* I - 1 if a collection, 0 otherwise */
6633 int i; /* Looping var */
6634 size_t bytes; /* Number of bytes */
6635 ipp_attribute_t *attr; /* Current attribute */
6636 ipp_tag_t group; /* Current group */
6637 _ipp_value_t *value; /* Current value */
6640 DEBUG_printf(("3ipp_length(ipp=%p, collection=%d)", (void *)ipp, collection));
6644 DEBUG_puts("4ipp_length: Returning 0 bytes");
6649 * Start with 8 bytes for the IPP message header...
6652 bytes = collection ? 0 : 8;
6655 * Then add the lengths of each attribute...
6658 group = IPP_TAG_ZERO;
6660 for (attr = ipp->attrs; attr != NULL; attr = attr->next)
6662 if (attr->group_tag != group && !collection)
6664 group = attr->group_tag;
6665 if (group == IPP_TAG_ZERO)
6668 bytes ++; /* Group tag */
6674 DEBUG_printf(("5ipp_length: attr->name=\"%s\", attr->num_values=%d, "
6675 "bytes=" CUPS_LLFMT, attr->name, attr->num_values, CUPS_LLCAST bytes));
6677 if ((attr->value_tag & ~IPP_TAG_CUPS_CONST) < IPP_TAG_EXTENSION)
6678 bytes += (size_t)attr->num_values;/* Value tag for each value */
6680 bytes += (size_t)(5 * attr->num_values);
6681 /* Value tag for each value */
6682 bytes += (size_t)(2 * attr->num_values);
6684 bytes += strlen(attr->name); /* Name */
6685 bytes += (size_t)(2 * attr->num_values);
6689 bytes += 5; /* Add membername overhead */
6691 switch (attr->value_tag & ~IPP_TAG_CUPS_CONST)
6693 case IPP_TAG_UNSUPPORTED_VALUE :
6694 case IPP_TAG_DEFAULT :
6695 case IPP_TAG_UNKNOWN :
6696 case IPP_TAG_NOVALUE :
6697 case IPP_TAG_NOTSETTABLE :
6698 case IPP_TAG_DELETEATTR :
6699 case IPP_TAG_ADMINDEFINE :
6702 case IPP_TAG_INTEGER :
6704 bytes += (size_t)(4 * attr->num_values);
6707 case IPP_TAG_BOOLEAN :
6708 bytes += (size_t)attr->num_values;
6713 case IPP_TAG_KEYWORD :
6715 case IPP_TAG_URISCHEME :
6716 case IPP_TAG_CHARSET :
6717 case IPP_TAG_LANGUAGE :
6718 case IPP_TAG_MIMETYPE :
6719 for (i = 0, value = attr->values;
6720 i < attr->num_values;
6722 if (value->string.text)
6723 bytes += strlen(value->string.text);
6727 bytes += (size_t)(11 * attr->num_values);
6730 case IPP_TAG_RESOLUTION :
6731 bytes += (size_t)(9 * attr->num_values);
6734 case IPP_TAG_RANGE :
6735 bytes += (size_t)(8 * attr->num_values);
6738 case IPP_TAG_TEXTLANG :
6739 case IPP_TAG_NAMELANG :
6740 bytes += (size_t)(4 * attr->num_values);
6741 /* Charset + text length */
6743 for (i = 0, value = attr->values;
6744 i < attr->num_values;
6747 if (value->string.language)
6748 bytes += strlen(value->string.language);
6750 if (value->string.text)
6751 bytes += strlen(value->string.text);
6755 case IPP_TAG_BEGIN_COLLECTION :
6756 for (i = 0, value = attr->values;
6757 i < attr->num_values;
6759 bytes += ipp_length(value->collection, 1);
6763 for (i = 0, value = attr->values;
6764 i < attr->num_values;
6766 bytes += (size_t)value->unknown.length;
6772 * Finally, add 1 byte for the "end of attributes" tag or 5 bytes
6773 * for the "end of collection" tag and return...
6781 DEBUG_printf(("4ipp_length: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST bytes));
6788 * 'ipp_read_http()' - Semi-blocking read on a HTTP connection...
6791 static ssize_t /* O - Number of bytes read */
6792 ipp_read_http(http_t *http, /* I - Client connection */
6793 ipp_uchar_t *buffer, /* O - Buffer for data */
6794 size_t length) /* I - Total length */
6796 ssize_t tbytes, /* Total bytes read */
6797 bytes; /* Bytes read this pass */
6800 DEBUG_printf(("7ipp_read_http(http=%p, buffer=%p, length=%d)", (void *)http, (void *)buffer, (int)length));
6803 * Loop until all bytes are read...
6806 for (tbytes = 0, bytes = 0;
6807 tbytes < (int)length;
6808 tbytes += bytes, buffer += bytes)
6810 DEBUG_printf(("9ipp_read_http: tbytes=" CUPS_LLFMT ", http->state=%d", CUPS_LLCAST tbytes, http->state));
6812 if (http->state == HTTP_STATE_WAITING)
6815 if (http->used == 0 && !http->blocking)
6818 * Wait up to 10 seconds for more data on non-blocking sockets...
6821 if (!httpWait(http, 10000))
6831 else if (http->used == 0 && http->timeout_value > 0)
6834 * Wait up to timeout seconds for more data on blocking sockets...
6837 if (!httpWait(http, (int)(1000 * http->timeout_value)))
6848 if ((bytes = httpRead2(http, (char *)buffer, length - (size_t)tbytes)) < 0)
6853 if (errno != EAGAIN && errno != EINTR)
6859 else if (bytes == 0)
6864 * Return the number of bytes read...
6867 if (tbytes == 0 && bytes < 0)
6870 DEBUG_printf(("8ipp_read_http: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST tbytes));
6877 * 'ipp_read_file()' - Read IPP data from a file.
6880 static ssize_t /* O - Number of bytes read */
6881 ipp_read_file(int *fd, /* I - File descriptor */
6882 ipp_uchar_t *buffer, /* O - Read buffer */
6883 size_t length) /* I - Number of bytes to read */
6886 return ((ssize_t)read(*fd, buffer, (unsigned)length));
6888 return (read(*fd, buffer, length));
6894 * 'ipp_set_error()' - Set a formatted, localized error string.
6898 ipp_set_error(ipp_status_t status, /* I - Status code */
6899 const char *format, /* I - Printf-style error string */
6900 ...) /* I - Additional arguments as needed */
6902 va_list ap; /* Pointer to additional args */
6903 char buffer[2048]; /* Message buffer */
6904 cups_lang_t *lang = cupsLangDefault();
6905 /* Current language */
6908 va_start(ap, format);
6909 vsnprintf(buffer, sizeof(buffer), _cupsLangString(lang, format), ap);
6912 _cupsSetError(status, buffer, 0);
6917 * 'ipp_set_value()' - Get the value element from an attribute, expanding it as
6921 static _ipp_value_t * /* O - IPP value element or NULL on error */
6922 ipp_set_value(ipp_t *ipp, /* IO - IPP message */
6923 ipp_attribute_t **attr, /* IO - IPP attribute */
6924 int element) /* I - Value number (0-based) */
6926 ipp_attribute_t *temp, /* New attribute pointer */
6927 *current, /* Current attribute in list */
6928 *prev; /* Previous attribute in list */
6929 int alloc_values; /* Allocated values */
6933 * If we are setting an existing value element, return it...
6938 if (temp->num_values <= 1)
6941 alloc_values = (temp->num_values + IPP_MAX_VALUES - 1) &
6942 ~(IPP_MAX_VALUES - 1);
6944 if (element < alloc_values)
6946 if (element >= temp->num_values)
6947 temp->num_values = element + 1;
6949 return (temp->values + element);
6953 * Otherwise re-allocate the attribute - we allocate in groups of IPP_MAX_VALUE
6954 * values when num_values > 1.
6957 if (alloc_values < IPP_MAX_VALUES)
6958 alloc_values = IPP_MAX_VALUES;
6960 alloc_values += IPP_MAX_VALUES;
6962 DEBUG_printf(("4ipp_set_value: Reallocating for up to %d values.",
6966 * Reallocate memory...
6969 if ((temp = realloc(temp, sizeof(ipp_attribute_t) + (size_t)(alloc_values - 1) * sizeof(_ipp_value_t))) == NULL)
6971 _cupsSetHTTPError(HTTP_STATUS_ERROR);
6972 DEBUG_puts("4ipp_set_value: Unable to resize attribute.");
6977 * Zero the new memory...
6980 memset(temp->values + temp->num_values, 0, (size_t)(alloc_values - temp->num_values) * sizeof(_ipp_value_t));
6985 * Reset pointers in the list...
6988 DEBUG_printf(("4debug_free: %p %s", (void *)*attr, temp->name));
6989 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));
6991 if (ipp->current == *attr && ipp->prev)
6994 * Use current "previous" pointer...
7002 * Find this attribute in the linked list...
7005 for (prev = NULL, current = ipp->attrs;
7006 current && current != *attr;
7007 prev = current, current = current->next);
7012 * This is a serious error!
7016 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
7017 _("IPP attribute is not a member of the message."), 1);
7018 DEBUG_puts("4ipp_set_value: Unable to find attribute in message.");
7028 ipp->current = temp;
7031 if (ipp->last == *attr)
7038 * Return the value element...
7041 if (element >= temp->num_values)
7042 temp->num_values = element + 1;
7044 return (temp->values + element);
7049 * 'ipp_write_file()' - Write IPP data to a file.
7052 static ssize_t /* O - Number of bytes written */
7053 ipp_write_file(int *fd, /* I - File descriptor */
7054 ipp_uchar_t *buffer, /* I - Data to write */
7055 size_t length) /* I - Number of bytes to write */
7058 return ((ssize_t)write(*fd, buffer, (unsigned)length));
7060 return (write(*fd, buffer, length));