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