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