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