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