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