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