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