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