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