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